1 """
2 Low level *Skype for Linux* interface implemented using *dbus-python* package.
3
4 This module handles the options that you can pass to `Skype.__init__`
5 for Linux machines when the transport is set to *DBus*. See below.
6
7 - ``RunMainLoop`` (bool) - If set to False, Skype4Py won't start the GLib main
8 loop. Otherwise it is started in a separate thread. The loop must be running for
9 Skype4Py events to work properly. Set this option to False if you plan to run the
10 loop yourself or if, for example, your GUI framework does it for you.
11
12 :requires: Skype for Linux 2.0 (beta) or newer.
13 """
14 __docformat__ = 'restructuredtext en'
15
16
17 import sys
18 import threading
19 import time
20 import warnings
21 import logging
22
23 from Skype4Py.api import Command, SkypeAPIBase, \
24 timeout2float, finalize_opts
25 from Skype4Py.enums import *
26 from Skype4Py.errors import SkypeAPIError
27 from Skype4Py.utils import cndexp
28
29
30 __all__ = ['SkypeAPI']
31
32
33 if getattr(sys, 'skype4py_setup', False):
34
35
36 - class dbus(object):
40 @staticmethod
42 return lambda *args, **kwargs: None
43 else:
44 import dbus
45 import dbus.service
46 from dbus.mainloop.glib import DBusGMainLoop
47 import gobject
51 """DBus object which exports a Notify method. This will be called by Skype for all
52 notifications with the notification string as a parameter. The Notify method of this
53 class calls in turn the callable passed to the constructor.
54 """
55
59
60 @dbus.service.method(dbus_interface='com.Skype.API.Client')
63
67 self.logger = logging.getLogger('Skype4Py.api.posix_dbus.SkypeAPI')
68 SkypeAPIBase.__init__(self)
69 self.run_main_loop = opts.pop('RunMainLoop', True)
70 finalize_opts(opts)
71 self.skype_in = self.skype_out = self.dbus_name_owner_watch = None
72
73
74 gobject.threads_init()
75
76
77
78 warnings.simplefilter('ignore')
79 try:
80 self.bus = dbus.SessionBus(mainloop=DBusGMainLoop())
81 finally:
82 warnings.simplefilter('default')
83
84 if self.run_main_loop:
85 self.mainloop = gobject.MainLoop()
86
88 self.logger.info('thread started')
89 if self.run_main_loop:
90 self.mainloop.run()
91 self.logger.info('thread finished')
92
94 if self.run_main_loop:
95 self.mainloop.quit()
96 self.skype_in = self.skype_out = None
97 if self.dbus_name_owner_watch is not None:
98 self.bus.remove_signal_receiver(self.dbus_name_owner_watch)
99 self.dbus_name_owner_watch = None
100 SkypeAPIBase.close(self)
101
106
108
109 self.dbus_name_owner_watch = self.bus.add_signal_receiver(self.dbus_name_owner_changed,
110 'NameOwnerChanged',
111 'org.freedesktop.DBus',
112 'org.freedesktop.DBus',
113 '/org/freedesktop/DBus',
114 arg0='com.Skype.API')
115
116 - def attach(self, timeout, wait=True):
117 self.acquire()
118 try:
119 try:
120 if not self.isAlive():
121 self.start_watcher()
122 self.start()
123 except AssertionError:
124 pass
125 try:
126 self.wait = True
127 t = threading.Timer(timeout2float(timeout), lambda: setattr(self, 'wait', False))
128 if wait:
129 t.start()
130 while self.wait:
131 if not wait:
132 self.wait = False
133 try:
134 if not self.skype_out:
135 self.skype_out = self.bus.get_object('com.Skype.API', '/com/Skype')
136 if not self.skype_in:
137 self.skype_in = SkypeNotify(self.bus, self.notify)
138 except dbus.DBusException:
139 if not wait:
140 break
141 time.sleep(1.0)
142 else:
143 break
144 else:
145 raise SkypeAPIError('Skype attach timeout')
146 finally:
147 t.cancel()
148 command = Command('NAME %s' % self.friendly_name, '', True, timeout)
149 if self.skype_out:
150 self.release()
151 try:
152 self.send_command(command)
153 finally:
154 self.acquire()
155 if command.Reply != 'OK':
156 self.skype_out = None
157 self.set_attachment_status(apiAttachRefused)
158 return
159 self.set_attachment_status(apiAttachSuccess)
160 finally:
161 self.release()
162 command = Command('PROTOCOL %s' % self.protocol, Blocking=True)
163 self.send_command(command)
164 self.protocol = int(command.Reply.rsplit(None, 1)[-1])
165
167 try:
168 self.bus.get_object('com.Skype.API', '/com/Skype')
169 return True
170 except dbus.DBusException:
171 return False
172
173 - def startup(self, minimized, nosplash):
174
175 if not self.is_running():
176 import os
177 if os.fork() == 0:
178 os.setsid()
179 os.execlp('skype')
180
182 import os
183 from signal import SIGINT
184 fh = os.popen('ps -o %p --no-heading -C skype')
185 pid = fh.readline().strip()
186 fh.close()
187 if pid:
188 os.kill(int(pid), SIGINT)
189 self.skype_in = self.skype_out = None
190
192 if not self.skype_out:
193 self.attach(command.Timeout)
194 self.push_command(command)
195 self.notifier.sending_command(command)
196 cmd = u'#%d %s' % (command.Id, command.Command)
197 self.logger.debug('sending %s', repr(cmd))
198 if command.Blocking:
199 if self.run_main_loop:
200 command._event = event = threading.Event()
201 else:
202 command._loop = loop = gobject.MainLoop()
203 command._set = False
204 else:
205 command._timer = timer = threading.Timer(command.timeout2float(), self.pop_command, (command.Id,))
206 try:
207 result = self.skype_out.Invoke(cmd)
208 except dbus.DBusException, err:
209 raise SkypeAPIError(str(err))
210 if result.startswith(u'#%d ' % command.Id):
211 self.notify(result)
212 if command.Blocking:
213 if self.run_main_loop:
214 event.wait(command.timeout2float())
215 if not event.isSet():
216 raise SkypeAPIError('Skype command timeout')
217 elif not command._set:
218 gobject.timeout_add_seconds(int(command.timeout2float()), loop.quit)
219 loop.run()
220 if not command._set:
221 raise SkypeAPIError('Skype command timeout')
222 else:
223 timer.start()
224
226 cmd = unicode(cmd)
227 self.logger.debug('received %s', repr(cmd))
228 if cmd.startswith(u'#'):
229 p = cmd.find(u' ')
230 command = self.pop_command(int(cmd[1:p]))
231 if command is not None:
232 command.Reply = cmd[p + 1:]
233 if command.Blocking:
234 if self.run_main_loop:
235 command._event.set()
236 else:
237 command._set = True
238 command._loop.quit()
239 else:
240 command._timer.cancel()
241 self.notifier.reply_received(command)
242 else:
243 self.notifier.notification_received(cmd[p + 1:])
244 else:
245 self.notifier.notification_received(cmd)
246
254