Commit 99163a70 authored by Dan Pascu's avatar Dan Pascu

Added screen sharing support

parent 92ab48d0
......@@ -14,4 +14,5 @@
^MANIFEST$
(^|/)\.directory$
(^|/)\.komodotools$
(^|/)blink/screensharing/_rfb.c$
(^|/)resources/icons/work/sandbox($|/)
......@@ -125,6 +125,8 @@ class Blink(QApplication):
self.chat_window.addAction(self.main_window.preferences_action)
self.chat_window.addAction(self.main_window.transfers_window_action)
self.chat_window.addAction(self.main_window.logs_window_action)
self.chat_window.addAction(self.main_window.received_files_window_action)
self.chat_window.addAction(self.main_window.screenshots_window_action)
self.ip_address_monitor = IPAddressMonitor()
self.log_manager = LogManager()
......
......@@ -646,6 +646,8 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
sliding_panels = True
__streamtypes__ = {'chat', 'screen-sharing', 'video'} # the stream types for which we show the chat window
def __init__(self, parent=None):
super(ChatWindow, self).__init__(parent)
with Resources.directory:
......@@ -732,6 +734,9 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.control_button.actions.disconnect = QAction("Disconnect", self, triggered=self._AH_Disconnect)
self.control_button.actions.add_audio = QAction("Add audio", self, triggered=self._AH_AddAudio)
self.control_button.actions.remove_audio = QAction("Remove audio", self, triggered=self._AH_RemoveAudio)
self.control_button.actions.share_my_screen = QAction("Share my screen", self, triggered=self._AH_ShareMyScreen)
self.control_button.actions.request_screen = QAction("Request screen", self, triggered=self._AH_RequestScreen)
self.control_button.actions.end_screen_sharing = QAction("End screen sharing", self, triggered=self._AH_EndScreenSharing)
self.control_button.actions.dump_session = QAction("Dump session", self, triggered=self._AH_DumpSession) # remove later -Dan
self.control_button.actions.main_window = QAction("Main Window", self, triggered=self._AH_MainWindow, shortcut='Ctrl+B', shortcutContext=Qt.ApplicationShortcut)
......@@ -852,7 +857,16 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
else:
menu.addAction(self.control_button.actions.disconnect)
if state == 'connected':
menu.addAction(self.control_button.actions.add_audio if 'audio' not in blink_session.streams else self.control_button.actions.remove_audio)
stream_types = blink_session.streams.types
if 'audio' not in stream_types:
menu.addAction(self.control_button.actions.add_audio)
elif stream_types != {'audio'} and not stream_types.intersection({'screen-sharing', 'video'}):
menu.addAction(self.control_button.actions.remove_audio)
if 'screen-sharing' not in stream_types:
menu.addAction(self.control_button.actions.request_screen)
menu.addAction(self.control_button.actions.share_my_screen)
elif stream_types != {'screen-sharing'}:
menu.addAction(self.control_button.actions.end_screen_sharing)
#menu.addAction(self.control_button.actions.dump_session) # remove this later -Dan
self.control_button.setMenu(menu)
......@@ -865,6 +879,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
have_session = blink_session.state in ('connecting/*', 'connected/*', 'ending')
have_audio = 'audio' in blink_session.streams
have_chat = 'chat' in blink_session.streams
have_screen = 'screen-sharing' in blink_session.streams
if update_visibility:
self.status_value_label.setEnabled(have_session)
......@@ -877,11 +892,13 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.audio_ice_status_value_label.setEnabled(have_audio)
self.chat_value_widget.setEnabled(have_chat)
self.chat_addresses_value_label.setEnabled(have_chat)
self.screen_value_widget.setEnabled(have_screen)
session_info = blink_session.info
audio_info = blink_session.info.streams.audio
video_info = blink_session.info.streams.video
chat_info = blink_session.info.streams.chat
screen_info = blink_session.info.streams.screen_sharing
if 'status' in elements and blink_session.state in ('initialized', 'connecting/*', 'connected/*', 'ended'):
state_map = {'initialized': 'Disconnected',
......@@ -954,6 +971,14 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
else:
self.chat_addresses_value_label.setText(u'N/A')
if screen_info.remote_address is not None and screen_info.mode == 'active':
self.screen_value_label.setText(u'Viewing remote')
elif screen_info.remote_address is not None and screen_info.mode == 'passive':
self.screen_value_label.setText(u'Sharing local')
else:
self.screen_value_label.setText(u'N/A')
self.screen_encryption_label.setVisible(screen_info.remote_address is not None and screen_info.transport=='tls')
if 'statistics' in elements:
self.duration_value_label.value = session_info.duration
self.audio_latency_graph.data = audio_info.latency
......@@ -1132,11 +1157,11 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.traffic_graph.update()
def _NH_BlinkSessionNewIncoming(self, notification):
if 'chat' in notification.sender.streams.types:
if notification.sender.streams.types.intersection(self.__streamtypes__):
self.show()
def _NH_BlinkSessionNewOutgoing(self, notification):
if 'chat' in notification.sender.stream_descriptions.types:
if notification.sender.stream_descriptions.types.intersection(self.__streamtypes__):
self.show()
def _NH_BlinkSessionDidReinitializeForIncoming(self, notification):
......@@ -1145,7 +1170,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
selection_model = self.session_list.selectionModel()
selection_model.select(model.index(position), selection_model.ClearAndSelect)
self.session_list.scrollTo(model.index(position), QListView.EnsureVisible) # or PositionAtCenter
if 'chat' in notification.sender.streams.types:
if notification.sender.streams.types.intersection(self.__streamtypes__):
self.show()
def _NH_BlinkSessionDidReinitializeForOutgoing(self, notification):
......@@ -1154,7 +1179,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
selection_model = self.session_list.selectionModel()
selection_model.select(model.index(position), selection_model.ClearAndSelect)
self.session_list.scrollTo(model.index(position), QListView.EnsureVisible) # or PositionAtCenter
if 'chat' in notification.sender.stream_descriptions.types:
if notification.sender.stream_descriptions.types.intersection(self.__streamtypes__):
self.show()
# use BlinkSessionNewIncoming/Outgoing to show the chat window if there is a chat stream available (like with reinitialize) instead of using the sessionAdded signal from the model -Dan
......@@ -1163,7 +1188,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
# different class for them that posts different notifications. in that case we can do in in NewIncoming/Outgoing -Dan
def _NH_BlinkSessionWillAddStream(self, notification):
if notification.data.stream.type == 'chat':
if notification.data.stream.type in self.__streamtypes__:
self.show()
def _NH_BlinkSessionDidRemoveStream(self, notification):
......@@ -1421,6 +1446,21 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
def _AH_RemoveAudio(self):
self.selected_session.blink_session.remove_stream(self.selected_session.blink_session.streams.get('audio'))
def _AH_RequestScreen(self):
if 'audio' in self.selected_session.blink_session.streams:
self.selected_session.blink_session.add_stream(StreamDescription('screen-sharing', mode='viewer'))
else:
self.selected_session.blink_session.add_streams([StreamDescription('screen-sharing', mode='viewer'), StreamDescription('audio')])
def _AH_ShareMyScreen(self):
if 'audio' in self.selected_session.blink_session.streams:
self.selected_session.blink_session.add_stream(StreamDescription('screen-sharing', mode='server'))
else:
self.selected_session.blink_session.add_streams([StreamDescription('screen-sharing', mode='server'), StreamDescription('audio')])
def _AH_EndScreenSharing(self):
self.selected_session.blink_session.remove_stream(self.selected_session.blink_session.streams.get('screen-sharing'))
def _AH_DumpSession(self):
blink_session = self.selected_session.blink_session
print "state: %r" % blink_session.state
......
......@@ -114,6 +114,13 @@ class ChatWindowSettings(SettingsGroup):
font_size = Setting(type=int, default=None, nillable=True)
class BlinkScreenSharingSettings(SettingsGroup):
screenshots_directory = Setting(type=Path, default=Path(os.path.expanduser('~/Downloads')))
scale = Setting(type=bool, default=True)
open_fullscreen = Setting(type=bool, default=False)
open_viewonly = Setting(type=bool, default=False)
class BlinkPresenceSettings(SettingsGroup):
current_state = Setting(type=PresenceState, default=PresenceState('Available'))
state_history = Setting(type=PresenceStateList, default=PresenceStateList())
......@@ -126,4 +133,5 @@ class BlinkSettings(SettingsObject):
chat_window = ChatWindowSettings
presence = BlinkPresenceSettings
screen_sharing = BlinkScreenSharingSettings
......@@ -3060,8 +3060,8 @@ class ContactListView(QListView):
menu.addAction(self.actions.start_chat_session)
#menu.addAction(self.actions.send_sms)
menu.addAction(self.actions.send_files)
#menu.addAction(self.actions.request_screen)
#menu.addAction(self.actions.share_my_screen)
menu.addAction(self.actions.request_screen)
menu.addAction(self.actions.share_my_screen)
menu.addSeparator()
menu.addAction(self.actions.add_group)
menu.addAction(self.actions.add_contact)
......@@ -3307,10 +3307,14 @@ class ContactListView(QListView):
session_manager.send_file(contact, contact.uri, filename)
def _AH_RequestScreen(self):
pass
contact = self.selectionModel().selectedIndexes()[0].data(Qt.UserRole)
session_manager = SessionManager()
session_manager.create_session(contact, contact.uri, [StreamDescription('screen-sharing', mode='viewer'), StreamDescription('audio')])
def _AH_ShareMyScreen(self):
pass
contact = self.selectionModel().selectedIndexes()[0].data(Qt.UserRole)
session_manager = SessionManager()
session_manager.create_session(contact, contact.uri, [StreamDescription('screen-sharing', mode='server'), StreamDescription('audio')])
def _DH_ApplicationXBlinkGroupList(self, event, index, rect, item):
model = self.model()
......@@ -3459,8 +3463,8 @@ class ContactSearchListView(QListView):
menu.addAction(self.actions.start_chat_session)
#menu.addAction(self.actions.send_sms)
menu.addAction(self.actions.send_files)
#menu.addAction(self.actions.request_screen)
#menu.addAction(self.actions.share_my_screen)
menu.addAction(self.actions.request_screen)
menu.addAction(self.actions.share_my_screen)
menu.addSeparator()
menu.addAction(self.actions.edit_item)
menu.addAction(self.actions.delete_item)
......@@ -3640,10 +3644,14 @@ class ContactSearchListView(QListView):
session_manager.send_file(contact, contact.uri, filename)
def _AH_RequestScreen(self):
pass
contact = self.selectionModel().selectedIndexes()[0].data(Qt.UserRole)
session_manager = SessionManager()
session_manager.create_session(contact, contact.uri, [StreamDescription('screen-sharing', mode='viewer'), StreamDescription('audio')])
def _AH_ShareMyScreen(self):
pass
contact = self.selectionModel().selectedIndexes()[0].data(Qt.UserRole)
session_manager = SessionManager()
session_manager.create_session(contact, contact.uri, [StreamDescription('screen-sharing', mode='server'), StreamDescription('audio')])
def _DH_TextUriList(self, event, index, rect, item):
if index.isValid():
......@@ -3736,8 +3744,8 @@ class ContactDetailView(QListView):
menu.addAction(self.actions.start_chat_session)
#menu.addAction(self.actions.send_sms)
menu.addAction(self.actions.send_files)
#menu.addAction(self.actions.request_screen)
#menu.addAction(self.actions.share_my_screen)
menu.addAction(self.actions.request_screen)
menu.addAction(self.actions.share_my_screen)
menu.addSeparator()
if isinstance(selected_item, ContactURI) and model.contact_detail.editable:
menu.addAction(self.actions.make_uri_default)
......@@ -3875,10 +3883,26 @@ class ContactDetailView(QListView):
session_manager.send_file(contact, selected_uri, filename)
def _AH_RequestScreen(self):
pass
contact = self.contact_list.selectionModel().selectedIndexes()[0].data(Qt.UserRole)
selected_indexes = self.selectionModel().selectedIndexes()
item = selected_indexes[0].data(Qt.UserRole) if selected_indexes else None
if isinstance(item, ContactURI):
selected_uri = item.uri
else:
selected_uri = contact.uri
session_manager = SessionManager()
session_manager.create_session(contact, selected_uri, [StreamDescription('screen-sharing', mode='viewer'), StreamDescription('audio')])
def _AH_ShareMyScreen(self):
pass
contact = self.contact_list.selectionModel().selectedIndexes()[0].data(Qt.UserRole)
selected_indexes = self.selectionModel().selectedIndexes()
item = selected_indexes[0].data(Qt.UserRole) if selected_indexes else None
if isinstance(item, ContactURI):
selected_uri = item.uri
else:
selected_uri = contact.uri
session_manager = SessionManager()
session_manager.create_session(contact, selected_uri, [StreamDescription('screen-sharing', mode='server'), StreamDescription('audio')])
def _DH_ApplicationXBlinkSession(self, event, index, rect, item):
event.ignore(rect)
......
......@@ -205,6 +205,8 @@ class MainWindow(base_class, ui_class):
self.chat_window_action.triggered.connect(self._AH_ChatWindowActionTriggered)
self.transfers_window_action.triggered.connect(self._AH_TransfersWindowActionTriggered)
self.logs_window_action.triggered.connect(self._AH_LogsWindowActionTriggered)
self.received_files_window_action.triggered.connect(self._AH_ReceivedFilesWindowActionTriggered)
self.screenshots_window_action.triggered.connect(self._AH_ScreenshotsWindowActionTriggered)
def setupUi(self):
super(MainWindow, self).setupUi(self)
......@@ -216,6 +218,11 @@ class MainWindow(base_class, ui_class):
self.input_devices_group = QActionGroup(self)
self.alert_devices_group = QActionGroup(self)
self.request_screen_action = QAction('Request screen', self, triggered=self._AH_RequestScreenActionTriggered)
self.share_my_screen_action = QAction('Share my screen', self, triggered=self._AH_ShareMyScreenActionTriggered)
self.screen_sharing_button.addAction(self.request_screen_action)
self.screen_sharing_button.addAction(self.share_my_screen_action)
# adjust search box height depending on theme as the value set in designer isn't suited for all themes
search_box = self.search_box
option = QStyleOptionFrameV2()
......@@ -251,7 +258,7 @@ class MainWindow(base_class, ui_class):
def enable_call_buttons(self, enabled):
self.audio_call_button.setEnabled(enabled)
self.chat_session_button.setEnabled(enabled)
self.screen_sharing_button.setEnabled(False)
self.screen_sharing_button.setEnabled(enabled)
def load_audio_devices(self):
settings = SIPSimpleSettings()
......@@ -386,6 +393,18 @@ class MainWindow(base_class, ui_class):
makedirs(directory)
QDesktopServices.openUrl(QUrl.fromLocalFile(directory))
def _AH_ReceivedFilesWindowActionTriggered(self, checked):
settings = SIPSimpleSettings()
directory = settings.file_transfer.directory.normalized
makedirs(directory)
QDesktopServices.openUrl(QUrl.fromLocalFile(directory))
def _AH_ScreenshotsWindowActionTriggered(self, checked):
settings = BlinkSettings()
directory = settings.screen_sharing.screenshots_directory.normalized
makedirs(directory)
QDesktopServices.openUrl(QUrl.fromLocalFile(directory))
def _AH_VoicemailActionTriggered(self, action, checked):
account = action.data()
contact, contact_uri = URIUtils.find_contact(account.voicemail_uri, display_name='Voicemail')
......@@ -496,6 +515,34 @@ class MainWindow(base_class, ui_class):
session_manager = SessionManager()
session_manager.create_session(contact, contact_uri, [StreamDescription('chat')], connect=False)
def _AH_RequestScreenActionTriggered(self):
list_view = self.contact_list if self.contacts_view.currentWidget() is self.contact_list_panel else self.search_list
if list_view.detail_view.isVisible():
list_view.detail_view._AH_RequestScreen()
else:
selected_indexes = list_view.selectionModel().selectedIndexes()
if selected_indexes:
contact = selected_indexes[0].data(Qt.UserRole)
contact_uri = contact.uri
else:
contact, contact_uri = URIUtils.find_contact(self.search_box.text())
session_manager = SessionManager()
session_manager.create_session(contact, contact_uri, [StreamDescription('screen-sharing', mode='viewer'), StreamDescription('audio')])
def _AH_ShareMyScreenActionTriggered(self):
list_view = self.contact_list if self.contacts_view.currentWidget() is self.contact_list_panel else self.search_list
if list_view.detail_view.isVisible():
list_view.detail_view._AH_ShareMyScreen()
else:
selected_indexes = list_view.selectionModel().selectedIndexes()
if selected_indexes:
contact = selected_indexes[0].data(Qt.UserRole)
contact_uri = contact.uri
else:
contact, contact_uri = URIUtils.find_contact(self.search_box.text())
session_manager = SessionManager()
session_manager.create_session(contact, contact_uri, [StreamDescription('screen-sharing', mode='server'), StreamDescription('audio')])
def _SH_BreakConference(self):
active_session = self.session_list.selectionModel().selectedIndexes()[0].data(Qt.UserRole)
self.session_model.breakConference(active_session.client_conference)
......
......@@ -286,6 +286,12 @@ class PreferencesWindow(base_class, ui_class):
self.session_info_style_button.clicked.connect(self._SH_SessionInfoStyleButtonClicked)
self.traffic_units_button.clicked.connect(self._SH_TrafficUnitsButtonClicked)
# Screen sharing
self.screenshots_directory_browse_button.clicked.connect(self._SH_ScreenshotsDirectoryBrowseButtonClicked)
self.screen_sharing_scale_button.clicked.connect(self._SH_ScreenSharingScaleButtonClicked)
self.screen_sharing_fullscreen_button.clicked.connect(self._SH_ScreenSharingFullscreenButtonClicked)
self.screen_sharing_viewonly_button.clicked.connect(self._SH_ScreenSharingViewonlyButtonClicked)
# File transfer
self.download_directory_browse_button.clicked.connect(self._SH_DownloadDirectoryBrowseButtonClicked)
self.file_transfer_alert_button.clicked.connect(self._SH_FileTransferAlertButtonClicked)
......@@ -591,6 +597,12 @@ class PreferencesWindow(base_class, ui_class):
self.session_info_style_button.setChecked(blink_settings.chat_window.session_info.alternate_style)
self.traffic_units_button.setChecked(blink_settings.chat_window.session_info.bytes_per_second)
# Screen sharing settings
self.screenshots_directory_editor.setText(blink_settings.screen_sharing.screenshots_directory or u'')
self.screen_sharing_scale_button.setChecked(blink_settings.screen_sharing.scale)
self.screen_sharing_fullscreen_button.setChecked(blink_settings.screen_sharing.open_fullscreen)
self.screen_sharing_viewonly_button.setChecked(blink_settings.screen_sharing.open_viewonly)
# File transfer settings
self.download_directory_editor.setText(settings.file_transfer.directory or u'')
self.file_transfer_alert_button.setChecked(settings.sounds.play_file_alerts)
......@@ -1290,6 +1302,34 @@ class PreferencesWindow(base_class, ui_class):
settings.chat_window.session_info.bytes_per_second = checked
settings.save()
# Screen sharing signal handlers
def _SH_ScreenshotsDirectoryBrowseButtonClicked(self, checked):
# TODO: open the file selection dialog in non-modal mode. Same for the one for TLS CA list and the IconSelector from contacts. -Dan
settings = BlinkSettings()
directory = settings.screen_sharing.screenshots_directory or os.path.expanduser('~')
directory = QFileDialog.getExistingDirectory(self, u'Select Screenshots Directory', directory) or None
if directory is not None:
directory = os.path.normpath(directory)
if directory != settings.screen_sharing.screenshots_directory:
self.screenshots_directory_editor.setText(directory)
settings.screen_sharing.screenshots_directory = directory
settings.save()
def _SH_ScreenSharingScaleButtonClicked(self, checked):
settings = BlinkSettings()
settings.screen_sharing.scale = checked
settings.save()
def _SH_ScreenSharingFullscreenButtonClicked(self, checked):
settings = BlinkSettings()
settings.screen_sharing.open_fullscreen = checked
settings.save()
def _SH_ScreenSharingViewonlyButtonClicked(self, checked):
settings = BlinkSettings()
settings.screen_sharing.open_viewonly = checked
settings.save()
# File transfer signal handlers
def _SH_DownloadDirectoryBrowseButtonClicked(self, checked):
# TODO: open the file selection dialog in non-modal mode. Same for the one for TLS CA list and the IconSelector from contacts. -Dan
......
......@@ -93,8 +93,8 @@ class BlinkPresenceState(object):
service.capabilities.text = False
service.capabilities.message = True
service.capabilities.file_transfer = True
service.capabilities.screen_sharing_server = False
service.capabilities.screen_sharing_client = False
service.capabilities.screen_sharing_server = True
service.capabilities.screen_sharing_client = True
service.display_name = self.account.display_name or None
service.icon = "%s#blink-icon%s" % (self.account.xcap.icon.url, self.account.xcap.icon.etag) if self.account.xcap.icon is not None else None
service.device_info = pidf.DeviceInfo(instance_id, description=hostname, user_agent=settings.user_agent)
......
# Copyright (C) 2014 AG Projects. See LICENSE for details.
#
__all__ = ['ScreensharingWindow', 'VNCViewer', 'VNCClient', 'RFBSettings', 'ServerDefault', 'TrueColor', 'HighColor', 'LowColor']
from blink.screensharing.vncclient import VNCClient, RFBSettings, ServerDefault, TrueColor, HighColor, LowColor
from blink.screensharing.vncviewer import ScreensharingWindow, VNCViewer
This diff is collapsed.
# Copyright (C) 2014 AG Projects. See LICENSE for details.
#
__all__ = ['VNCClient', 'RFBSettings', 'ServerDefault', 'TrueColor', 'HighColor', 'LowColor']
from PyQt4.QtCore import QObject, QSize, QSocketNotifier, QThread, pyqtSignal
from PyQt4.QtGui import QApplication
from application.notification import NotificationCenter
from application.python import Null
from application.python.descriptor import WriteOnceAttribute
from blink.event import EventBase
from blink.screensharing._rfb import RFBClient, RFBClientError
class RFBSettings(object):
depth = WriteOnceAttribute()
quality = WriteOnceAttribute()
compression = WriteOnceAttribute()
encodings = WriteOnceAttribute()
def __init__(self, depth, quality, compression, encodings):
if depth not in (8, 16, 24, 32, None):
raise ValueError("invalid depth value: %r (should be one of 8, 16, 24, 32 or None)" % depth)
allowed_levels = range(10)
if quality not in allowed_levels:
raise ValueError("invalid quality value: %r (should be between 0..9)" % quality)
if compression not in allowed_levels:
raise ValueError("invalid compression value: %r (should be between 0..9)" % compression)
if not isinstance(encodings, str) or not encodings:
raise ValueError("invalid encodings value: %r (should be a non-empty string)" % encodings)
self.depth = depth
self.quality = quality
self.compression = compression
self.encodings = encodings
def __eq__(self, other):
if isinstance(other, RFBSettings):
return (self.depth, self.quality, self.compression, self.encodings) == (other.depth, other.quality, other.compression, other.encodings)
return NotImplemented
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return '{0.__class__.__name__}(depth={0.depth!r}, quality={0.quality!r}, compression={0.compression!r}, encodings={0.encodings!r})'.format(self)
ServerDefault = RFBSettings(depth=None, quality=9, compression=9, encodings='copyrect zlib zrle ultra hextile corre rre raw')
TrueColor = RFBSettings(depth=24, quality=9, compression=9, encodings='copyrect zlib zrle ultra hextile corre rre raw')
HighColor = RFBSettings(depth=16, quality=9, compression=9, encodings='copyrect zlib zrle ultra hextile corre rre raw')
LowColor = RFBSettings(depth=8, quality=9, compression=9, encodings='copyrect zlib zrle ultra hextile corre rre raw')
class RFBConfigureClientEvent(EventBase):
pass
class RFBKeyEvent(EventBase):
def __init__(self, key, down):
super(RFBKeyEvent, self).__init__()
self.key = key
self.down = down
class RFBMouseEvent(EventBase):
def __init__(self, x, y, button_mask):
super(RFBMouseEvent, self).__init__()
self.x = x
self.y = y
self.button_mask = button_mask
class RFBCutTextEvent(EventBase):
def __init__(self, text):
super(RFBCutTextEvent, self).__init__()
self.text = text
class VNCClient(QObject):
started = pyqtSignal()
finished = pyqtSignal()
imageSizeChanged = pyqtSignal(QSize)
imageChanged = pyqtSignal(int, int, int, int)
passwordRequested = pyqtSignal(bool)
textCut = pyqtSignal(unicode)
def __init__(self, host, port, settings, parent=None):
super(VNCClient, self).__init__(parent)
self.thread = QThread()
self.moveToThread(self.thread)
self.host = host
self.port = port
self.settings = settings
self.username = None
self.password = None
self.rfb_client = None
self.socket_notifier = None
self.thread.started.connect(self._SH_ThreadStarted)
self.thread.finished.connect(self._SH_ThreadFinished)
def _get_settings(self):
return self.__dict__['settings']
def _set_settings(self, settings):
old_settings = self.__dict__.get('settings', None)
if settings == old_settings:
return
self.__dict__['settings'] = settings
if self.thread.isRunning():
QApplication.postEvent(self, RFBConfigureClientEvent())
settings = property(_get_settings, _set_settings)
del _get_settings, _set_settings
@property
def image(self):
return self.rfb_client.image if self.rfb_client is not None else None
def start(self):
self.thread.start()
def stop(self):
self.thread.quit()
def key_event(self, key, down):
if self.thread.isRunning():
QApplication.postEvent(self, RFBKeyEvent(key, down))
def mouse_event(self, x, y, button_mask):
if self.thread.isRunning():
QApplication.postEvent(self, RFBMouseEvent(x, y, button_mask))
def cut_text_event(self, text):
if text and self.thread.isRunning():
QApplication.postEvent(self, RFBCutTextEvent(text))
def _SH_ThreadStarted(self):
self.started.emit()
notification_center = NotificationCenter()
notification_center.post_notification('VNCClientWillStart', sender=self)
self.rfb_client = RFBClient(parent=self)
try:
self.rfb_client.connect()
except RFBClientError:
self.thread.quit()
else:
self.socket_notifier = QSocketNotifier(self.rfb_client.socket, QSocketNotifier.Read, self)
self.socket_notifier.activated.connect(self._SH_SocketNotifierActivated)
notification_center.post_notification('VNCClientDidStart', sender=self)
def _SH_ThreadFinished(self):
self.finished.emit()
notification_center = NotificationCenter()
notification_center.post_notification('VNCClientWillEnd', sender=self)
if self.socket_notifier is not None:
self.socket_notifier.activated.disconnect(self._SH_SocketNotifierActivated)
self.socket_notifier = None
self.rfb_client = None
notification_center.post_notification('VNCClientDidEnd', sender=self)
def _SH_SocketNotifierActivated(self, sock):
self.socket_notifier.setEnabled(False)
try:
self.rfb_client.handle_server_message()
except RFBClientError:
self.thread.quit()
else:
self.socket_notifier.setEnabled(True)
def _SH_ConfigureRFBClient(self):
if self.rfb_client is not None:
self.rfb_client.configure()
def customEvent(self, event):
handler = getattr(self, '_EH_%s' % event.name, Null)
handler(event)
def _EH_RFBConfigureClientEvent(self, event):
if self.rfb_client is not None:
self.rfb_client.configure()
def _EH_RFBKeyEvent(self, event):
if self.rfb_client is not None:
self.rfb_client.send_key_event(event.key, event.down)
def _EH_RFBMouseEvent(self, event):
if self.rfb_client is not None:
self.rfb_client.send_pointer_event(event.x, event.y, event.button_mask)
def _EH_RFBCutTextEvent(self, event):
if self.rfb_client is not None:
self.rfb_client.send_client_cut_text(event.text)
This diff is collapsed.
This diff is collapsed.
#!/bin/sh
python setup.py build_ext --inplace $@
......@@ -657,12 +657,12 @@ padding: 2px;</string>
</property>
<property name="icon">
<iconset>
<normaloff>icons/green-handset.png</normaloff>icons/green-handset.png</iconset>
<normaloff>icons/handset.svg</normaloff>icons/handset.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
......@@ -690,8 +690,8 @@ padding: 2px;</string>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="popupMode">
......@@ -718,12 +718,12 @@ padding: 2px;</string>
</property>
<property name="icon">
<iconset>
<normaloff>icons/display18.png</normaloff>icons/display18.png</iconset>
<normaloff>icons/work/screen.svg</normaloff>icons/work/screen.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="popupMode">
......@@ -1052,6 +1052,8 @@ padding: 2px;</string>
<addaction name="chat_window_action"/>
<addaction name="transfers_window_action"/>
<addaction name="logs_window_action"/>
<addaction name="received_files_window_action"/>
<addaction name="screenshots_window_action"/>
</widget>
<addaction name="blink_menu"/>
<addaction name="call_menu"/>
......@@ -1276,6 +1278,28 @@ padding: 2px;</string>
<string>Play a sound for &amp;received messages</string>
</property>
</action>
<action name="received_files_window_action">
<property name="text">
<string>Received &amp;Files</string>
</property>
<property name="shortcut">
<string>Ctrl+D</string>
</property>
<property name="shortcutContext">
<enum>Qt::ApplicationShortcut</enum>
</property>
</action>
<action name="screenshots_window_action">
<property name="text">
<string>&amp;Screenshots</string>
</property>
<property name="shortcut">
<string>Ctrl+E</string>
</property>
<property name="shortcutContext">
<enum>Qt::ApplicationShortcut</enum>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
......
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="close-active.svg"
inkscape:version="0.48.4 r9939"
xml:space="preserve"
width="96px"
viewBox="0 0 96 96"
version="1.1"
id="bigger"
height="96px"
enable-background="new 0 0 96 96"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9"><linearGradient
id="linearGradient3983"><stop
style="stop-color:#00ff00;stop-opacity:1;"
offset="0"
id="stop3985" /><stop
style="stop-color:#00d000;stop-opacity:1;"
offset="1"
id="stop3987" /></linearGradient><linearGradient
id="linearGradient12315"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop12317" /><stop
style="stop-color:#d30000;stop-opacity:1;"
offset="1"
id="stop12319" /></linearGradient><linearGradient
id="linearGradient11765"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop11767" /><stop
style="stop-color:#d00000;stop-opacity:1;"
offset="1"
id="stop11769" /></linearGradient><linearGradient
id="linearGradient11102"><stop
style="stop-color:#000000;stop-opacity:0.43137255;"
offset="0"
id="stop11104" /><stop
style="stop-color:#464646;stop-opacity:1;"
offset="1"
id="stop11106" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient11765"
id="radialGradient11771"
cx="12"
cy="18"
fx="12"
fy="18"
r="29.642775"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12315"
id="radialGradient12321"
cx="48"
cy="48"
fx="48"
fy="48"
r="44"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3983"
id="radialGradient3989"
cx="12"
cy="18"
fx="12"
fy="18"
r="29.642775"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="layer15"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
inkscape:groupmode="layer"
id="layer15"
inkscape:label="Close active"
style="display:inline"><path
sodipodi:type="arc"
style="fill:url(#radialGradient11771);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.38959551;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path11668"
sodipodi:cx="12"
sodipodi:cy="18"
sodipodi:rx="26.947977"
sodipodi:ry="26.947977"
d="m 38.947977,18 a 26.947977,26.947977 0 1 1 -53.895954,0 26.947977,26.947977 0 1 1 53.895954,0 z"
transform="matrix(1.4843415,0,0,1.4843415,30.187902,21.281853)" /><path
style="fill:none;stroke:#000000;stroke-width:7.99999952000000025;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 63.556404,32.443596 32.443596,63.556404"
id="path11670"
inkscape:connector-curvature="0" /><path
style="fill:none;stroke:#000000;stroke-width:7.99999952000000025;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 63.556404,63.556404 32.443596,32.443596"
id="path11670-8"
inkscape:connector-curvature="0" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="close.svg"
inkscape:version="0.48.4 r9939"
xml:space="preserve"
width="96px"
viewBox="0 0 96 96"
version="1.1"
id="bigger"
height="96px"
enable-background="new 0 0 96 96"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9"><linearGradient
id="linearGradient12315"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop12317" /><stop
style="stop-color:#d30000;stop-opacity:1;"
offset="1"
id="stop12319" /></linearGradient><linearGradient
id="linearGradient11765"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop11767" /><stop
style="stop-color:#d30000;stop-opacity:1;"
offset="1"
id="stop11769" /></linearGradient><linearGradient
id="linearGradient11102"><stop
style="stop-color:#000000;stop-opacity:0.43137255;"
offset="0"
id="stop11104" /><stop
style="stop-color:#464646;stop-opacity:1;"
offset="1"
id="stop11106" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient11765"
id="radialGradient11771"
cx="12"
cy="18"
fx="12"
fy="18"
r="29.642775"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12315"
id="radialGradient12321"
cx="48"
cy="48"
fx="48"
fy="48"
r="44"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="g12004"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
inkscape:label="Close"
id="g12004"
inkscape:groupmode="layer"
style="display:inline"><path
transform="matrix(1.4843415,0,0,1.4843415,30.187902,21.281853)"
d="m 38.947977,18 a 26.947977,26.947977 0 1 1 -53.895954,0 26.947977,26.947977 0 1 1 53.895954,0 z"
sodipodi:ry="26.947977"
sodipodi:rx="26.947977"
sodipodi:cy="18"
sodipodi:cx="12"
id="path12006"
style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.38959551000000037;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /><path
inkscape:connector-curvature="0"
id="path12008"
d="M 63.556404,32.443596 32.443596,63.556404"
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path12010"
d="M 63.556404,63.556404 32.443596,32.443596"
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 96 96"
height="96px"
id="bigger"
version="1.1"
viewBox="0 0 96 96"
width="96px"
xml:space="preserve"
inkscape:version="0.48.4 r9939"
sodipodi:docname="fullscreen-exit.svg"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="g7032"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
inkscape:groupmode="layer"
id="g7032"
inkscape:label="Fullscreen exit"
style="display:inline"><path
inkscape:connector-curvature="0"
id="path7034"
d="M 60,36 C 84.000001,12 84.000001,12 84.000001,12"
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 80,39.999999 c -24,0 -24,0 -24,0 l 0,-24"
id="path7036"
inkscape:connector-curvature="0" /><path
inkscape:connector-curvature="0"
id="path7038"
d="M 36.000001,59.999999 C 12,83.999999 12,83.999999 12,83.999999"
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 16.000001,56 c 24,0 24,0 24,0 l 0,24"
id="path7040"
inkscape:connector-curvature="0" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 96 96"
height="96px"
id="bigger"
version="1.1"
viewBox="0 0 96 96"
width="96px"
xml:space="preserve"
inkscape:version="0.48.4 r9939"
sodipodi:docname="fullscreen.svg"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="g6591"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
style="display:inline"
inkscape:label="Fullscreen"
id="g6591"
inkscape:groupmode="layer"><path
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 80,15.999999 c -24.000001,24 -24.000001,24 -24.000001,24"
id="path6595"
inkscape:connector-curvature="0" /><path
inkscape:connector-curvature="0"
id="path6597"
d="m 60,12 c 24,0 24,0 24,0 l 0,24"
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 16,80.000001 c 24.000001,-24 24.000001,-24 24.000001,-24"
id="path6601"
inkscape:connector-curvature="0" /><path
inkscape:connector-curvature="0"
id="path6603"
d="M 36,84 C 12,84 12,84 12,84 L 12,60"
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 50 50"
height="16"
id="Layer_1"
version="1.1"
viewBox="0 0 16 16"
width="16"
xml:space="preserve"
inkscape:version="0.48.4 r9939"
sodipodi:docname="handset.svg"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs9" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1782"
inkscape:window-height="1130"
id="namedview7"
showgrid="true"
inkscape:zoom="57.5"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1"><inkscape:grid
type="xygrid"
id="grid3756"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" /></sodipodi:namedview><rect
height="50"
width="50"
id="rect3"
x="0"
y="-34"
style="fill:none" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Handset"><path
d="m 5.5396163,9.252177 c 0,0 0.5563174,0.971756 0.6899968,1.226155 0.1341593,0.253679 0.1679992,0.516477 0.07056,0.676077 -0.1478397,0.241679 -1.4978334,2.271109 -1.6173529,2.441988 -0.1197594,0.17088 -0.4617578,0.253198 -0.7967963,0.01824 C 3.5507455,13.380878 2.2259516,12.301363 2.2401116,11.843925 2.2549916,11.385768 2.2967516,9.480896 5.8897746,5.888833 9.481838,2.29725 11.387189,2.25477 11.845107,2.24013 c 0.457919,-0.01464 1.537914,1.310154 1.772152,1.645192 0.233999,0.335519 0.14592,0.677997 -0.01872,0.797037 -0.18744,0.135359 -2.210391,1.469753 -2.44247,1.616632 -0.158159,0.10056 -0.422398,0.0636 -0.676316,-0.07056 C 10.225114,6.094992 9.2535981,5.538675 9.2535981,5.538675 c 0,0 -0.6688769,0.392158 -1.9948708,1.718392 -1.3262339,1.326474 -1.719112,1.99511 -1.719112,1.99511 z"
stroke-miterlimit="10"
id="path5"
inkscape:connector-curvature="0"
style="fill:#333333;fill-opacity:1;stroke:#222222;stroke-width:0.47999778;stroke-miterlimit:10;stroke-opacity:1" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="minimize-active.svg"
inkscape:version="0.48.4 r9939"
xml:space="preserve"
width="96px"
viewBox="0 0 96 96"
version="1.1"
id="bigger"
height="96px"
enable-background="new 0 0 96 96"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9"><linearGradient
id="linearGradient3983"><stop
style="stop-color:#00ff00;stop-opacity:1;"
offset="0"
id="stop3985" /><stop
style="stop-color:#00d000;stop-opacity:1;"
offset="1"
id="stop3987" /></linearGradient><linearGradient
id="linearGradient12315"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop12317" /><stop
style="stop-color:#d30000;stop-opacity:1;"
offset="1"
id="stop12319" /></linearGradient><linearGradient
id="linearGradient11765"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop11767" /><stop
style="stop-color:#d00000;stop-opacity:1;"
offset="1"
id="stop11769" /></linearGradient><linearGradient
id="linearGradient11102"><stop
style="stop-color:#000000;stop-opacity:0.43137255;"
offset="0"
id="stop11104" /><stop
style="stop-color:#464646;stop-opacity:1;"
offset="1"
id="stop11106" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient11765"
id="radialGradient11771"
cx="12"
cy="18"
fx="12"
fy="18"
r="29.642775"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12315"
id="radialGradient12321"
cx="48"
cy="48"
fx="48"
fy="48"
r="44"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3983"
id="radialGradient3989"
cx="12"
cy="18"
fx="12"
fy="18"
r="29.642775"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="g3101"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
inkscape:label="Minimize active"
id="g3101"
inkscape:groupmode="layer"
style="display:inline"><path
transform="matrix(1.4843415,0,0,1.4843415,30.187902,21.281853)"
d="m 38.947977,18 a 26.947977,26.947977 0 1 1 -53.895954,0 26.947977,26.947977 0 1 1 53.895954,0 z"
sodipodi:ry="26.947977"
sodipodi:rx="26.947977"
sodipodi:cy="18"
sodipodi:cx="12"
id="path3103"
style="fill:url(#radialGradient3989);fill-opacity:1;stroke:#000000;stroke-width:5.38959551;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /><path
inkscape:connector-curvature="0"
id="path3105"
d="M 70.000078,48 25.999922,48"
style="fill:none;stroke:#000000;stroke-width:7.99999952000000025;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="minimize.svg"
inkscape:version="0.48.4 r9939"
xml:space="preserve"
width="96px"
viewBox="0 0 96 96"
version="1.1"
id="bigger"
height="96px"
enable-background="new 0 0 96 96"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9"><linearGradient
id="linearGradient3983"><stop
style="stop-color:#00ff00;stop-opacity:1;"
offset="0"
id="stop3985" /><stop
style="stop-color:#00d000;stop-opacity:1;"
offset="1"
id="stop3987" /></linearGradient><linearGradient
id="linearGradient12315"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop12317" /><stop
style="stop-color:#d30000;stop-opacity:1;"
offset="1"
id="stop12319" /></linearGradient><linearGradient
id="linearGradient11765"><stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop11767" /><stop
style="stop-color:#d00000;stop-opacity:1;"
offset="1"
id="stop11769" /></linearGradient><linearGradient
id="linearGradient11102"><stop
style="stop-color:#000000;stop-opacity:0.43137255;"
offset="0"
id="stop11104" /><stop
style="stop-color:#464646;stop-opacity:1;"
offset="1"
id="stop11106" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient11765"
id="radialGradient11771"
cx="12"
cy="18"
fx="12"
fy="18"
r="29.642775"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12315"
id="radialGradient12321"
cx="48"
cy="48"
fx="48"
fy="48"
r="44"
gradientUnits="userSpaceOnUse" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3983"
id="radialGradient3989"
cx="12"
cy="18"
fx="12"
fy="18"
r="29.642775"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="g3082"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
style="display:inline"
inkscape:groupmode="layer"
id="g3082"
inkscape:label="Minimize"><path
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:5.38959551;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3084"
sodipodi:cx="12"
sodipodi:cy="18"
sodipodi:rx="26.947977"
sodipodi:ry="26.947977"
d="m 38.947977,18 a 26.947977,26.947977 0 1 1 -53.895954,0 26.947977,26.947977 0 1 1 53.895954,0 z"
transform="matrix(1.4843415,0,0,1.4843415,30.187902,21.281853)" /><path
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 70.000078,48 25.999922,48"
id="path3088"
inkscape:connector-curvature="0" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 96 96"
height="96px"
id="bigger"
version="1.1"
viewBox="0 0 96 96"
width="96px"
xml:space="preserve"
inkscape:version="0.48.4 r9939"
sodipodi:docname="scale.svg"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="g4423"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
style="display:inline"
inkscape:label="Scale"
id="g4423"
inkscape:groupmode="layer"><path
inkscape:connector-curvature="0"
id="path4425"
d="M 80,4 H 68 c -2.209,0 -4,1.791 -4,4 0,2.209 1.791,4 4,4 h 12 c 2.21,0 4,1.79 4,4 v 12 c 0,2.209 1.791,4 4,4 2.209,0 4,-1.791 4,-4 V 16 C 92,9.373 86.627,4 80,4 z" /><path
inkscape:connector-curvature="0"
id="path4427"
d="M 28,4 H 16 C 9.373,4 4,9.373 4,16 v 12 c 0,2.209 1.791,4 4,4 2.209,0 4,-1.791 4,-4 V 16 c 0,-2.21 1.79,-4 4,-4 h 12 c 2.209,0 4,-1.791 4,-4 0,-2.209 -1.791,-4 -4,-4 z" /><path
inkscape:connector-curvature="0"
id="path4429"
d="m 88,64 c -2.209,0 -4,1.791 -4,4 v 12 c 0,2.21 -1.79,4 -4,4 H 68 c -2.209,0 -4,1.791 -4,4 0,2.209 1.791,4 4,4 h 12 c 6.627,0 12,-5.373 12,-12 V 68 c 0,-2.209 -1.791,-4 -4,-4 z" /><path
inkscape:connector-curvature="0"
id="path4431"
d="M 28,84 H 16 c -2.21,0 -4,-1.79 -4,-4 V 68 c 0,-2.209 -1.791,-4 -4,-4 -2.209,0 -4,1.791 -4,4 v 12 c 0,6.627 5.373,12 12,12 h 12 c 2.209,0 4,-1.791 4,-4 0,-2.209 -1.791,-4 -4,-4 z" /><rect
ry="10"
y="32"
x="24"
height="32"
width="48"
id="rect4433"
style="fill:#000000;fill-opacity:1;stroke:none" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg6921"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="screen.svg">
<defs
id="defs6923" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="53.9375"
inkscape:cx="8"
inkscape:cy="8"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1667"
inkscape:window-height="1073"
inkscape:window-x="83"
inkscape:window-y="22"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid6929"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" />
</sodipodi:namedview>
<metadata
id="metadata6926">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Screen"
inkscape:groupmode="layer">
<rect
style="fill:#545454;fill-opacity:1;stroke:#333333;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
id="rect7463"
width="11"
height="7"
x="2.5"
y="2.5" />
<rect
style="fill:none;stroke:#333333;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect7465"
width="7"
height="0.19999999"
x="4.5"
y="13.3" />
<rect
style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.81199998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect7465-2"
width="4.1880002"
height="1.188"
x="-13.594"
y="7.4060001"
transform="matrix(0,-1,1,0,0,0)" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="screenshot.svg"
inkscape:version="0.48.4 r9939"
xml:space="preserve"
width="96px"
viewBox="0 0 96 96"
version="1.1"
id="bigger"
height="96px"
enable-background="new 0 0 96 96"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9"><linearGradient
id="linearGradient11102"><stop
style="stop-color:#000000;stop-opacity:0.43137255;"
offset="0"
id="stop11104" /><stop
style="stop-color:#464646;stop-opacity:1;"
offset="1"
id="stop11106" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient11102"
id="radialGradient11108"
cx="14.5"
cy="13"
fx="14.5"
fy="13"
r="10.060094"
gradientTransform="matrix(1,0,0,0.95029867,0,0.64611723)"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="g8714"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
inkscape:groupmode="layer"
id="g8714"
inkscape:label="Screenshot"
style="display:inline"><rect
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:1.6;display:inline"
id="rect9869"
width="42"
height="14"
x="27"
y="19"
rx="4.5778842"
ry="3.5097113" /><rect
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:6.00000048;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:1.6;display:inline"
id="rect9845"
width="82"
height="46"
x="7"
y="31"
ry="5.0138731"
rx="5.0138731" /><path
sodipodi:type="arc"
style="fill:none;stroke:#ffffff;stroke-width:3.72409511;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
id="path9681"
sodipodi:cx="14.5"
sodipodi:cy="13"
sodipodi:rx="8.5"
sodipodi:ry="8"
d="M 23,13 A 8.5,8 0 1 1 6,13 8.5,8 0 1 1 23,13 z"
transform="matrix(1.8235294,0,0,1.9375002,21.558824,28.812497)" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 96 96"
height="96px"
id="bigger"
version="1.1"
viewBox="0 0 96 96"
width="96px"
xml:space="preserve"
inkscape:version="0.48.4 r9939"
sodipodi:docname="viewonly.svg"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs9" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1623"
inkscape:window-height="1075"
id="namedview7"
showgrid="true"
inkscape:zoom="9.0104167"
inkscape:cx="48"
inkscape:cy="48"
inkscape:window-x="114"
inkscape:window-y="10"
inkscape:window-maximized="0"
inkscape:current-layer="g5470"
showguides="true"
inkscape:guide-bbox="true"><inkscape:grid
type="xygrid"
id="grid2988"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview><g
inkscape:label="View only"
id="g5470"
inkscape:groupmode="layer"
style="display:inline"><path
transform="translate(-2.0000003,-10.000001)"
d="m 66,46 a 28,28 0 1 1 -56,0 28,28 0 1 1 56,0 z"
sodipodi:ry="28"
sodipodi:rx="28"
sodipodi:cy="46"
sodipodi:cx="38"
id="path5472"
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
sodipodi:type="arc" /><path
inkscape:connector-curvature="0"
id="path5474"
d="M 88,87.999999 C 56,55.39701 56,56.000768 56,56.000768"
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g></svg>
\ No newline at end of file
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 50 50"
height="16"
id="Layer_1"
version="1.1"
viewBox="0 0 16 16"
width="16"
xml:space="preserve"
inkscape:version="0.48.4 r9939"
sodipodi:docname="handset.svg"><metadata
id="metadata11"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs9" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1782"
inkscape:window-height="1130"
id="namedview7"
showgrid="true"
inkscape:zoom="57.5"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1"><inkscape:grid
type="xygrid"
id="grid3756"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" /></sodipodi:namedview><rect
height="50"
width="50"
id="rect3"
x="0"
y="-34"
style="fill:none" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Handset"><path
d="m 5.5396163,9.252177 c 0,0 0.5563174,0.971756 0.6899968,1.226155 0.1341593,0.253679 0.1679992,0.516477 0.07056,0.676077 -0.1478397,0.241679 -1.4978334,2.271109 -1.6173529,2.441988 -0.1197594,0.17088 -0.4617578,0.253198 -0.7967963,0.01824 C 3.5507455,13.380878 2.2259516,12.301363 2.2401116,11.843925 2.2549916,11.385768 2.2967516,9.480896 5.8897746,5.888833 9.481838,2.29725 11.387189,2.25477 11.845107,2.24013 c 0.457919,-0.01464 1.537914,1.310154 1.772152,1.645192 0.233999,0.335519 0.14592,0.677997 -0.01872,0.797037 -0.18744,0.135359 -2.210391,1.469753 -2.44247,1.616632 -0.158159,0.10056 -0.422398,0.0636 -0.676316,-0.07056 C 10.225114,6.094992 9.2535981,5.538675 9.2535981,5.538675 c 0,0 -0.6688769,0.392158 -1.9948708,1.718392 -1.3262339,1.326474 -1.719112,1.99511 -1.719112,1.99511 z"
stroke-miterlimit="10"
id="path5"
inkscape:connector-curvature="0"
style="fill:#333333;fill-opacity:1;stroke:#222222;stroke-width:0.47999778;stroke-miterlimit:10;stroke-opacity:1" /></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg6921"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="screen.svg">
<defs
id="defs6923" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="53.9375"
inkscape:cx="8"
inkscape:cy="8"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1667"
inkscape:window-height="1073"
inkscape:window-x="83"
inkscape:window-y="22"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid6929"
empspacing="10"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5px"
spacingy="0.5px" />
</sodipodi:namedview>
<metadata
id="metadata6926">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Screen"
inkscape:groupmode="layer">
<rect
style="fill:#545454;fill-opacity:1;stroke:#333333;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
id="rect7463"
width="11"
height="7"
x="2.5"
y="2.5" />
<rect
style="fill:none;stroke:#333333;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect7465"
width="7"
height="0.19999999"
x="4.5"
y="13.3" />
<rect
style="fill:#333333;fill-opacity:1;stroke:#333333;stroke-width:0.81199998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect7465-2"
width="4.1880002"
height="1.188"
x="-13.594"
y="7.4060001"
transform="matrix(0,-1,1,0,0,0)" />
</g>
</svg>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/env python
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
from itertools import chain
import glob
import os
......@@ -32,6 +35,7 @@ setup(name = "blink",
"Programming Language :: Python"
],
packages = find_packages('blink'),
ext_modules = cythonize([Extension(name="blink.screensharing._rfb", sources=["blink/screensharing/_rfb.pyx"], libraries=["vncclient"])]),
data_files = list_resources('resources', destination_directory='share/blink'),
scripts = ['bin/blink']
)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment