From ecd25c96cdb83f2db85490589c761a3253f40681 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=B4natas=20Isvi?= Date: Thu, 4 Jun 2009 02:44:51 -0400 Subject: [PATCH] commit do server --- .../connection/bluetoothconnectionmanager.py | 215 ++++++++++++++ .../connection/genericconnectionmanager.py | 42 +++ pcremote-server/connection/iconnection.py | 167 +++++++++++ .../connection/wirelessconnectionmanager.py | 43 +++ pcremote-server/exceptions/exception.py | 52 ++++ pcremote-server/images/28x.png | Bin 0 -> 4474 bytes pcremote-server/images/64x.png | Bin 0 -> 9438 bytes pcremote-server/images/PCR_off.bmp | Bin 0 -> 1080 bytes pcremote-server/images/PCR_on.bmp | Bin 0 -> 1080 bytes pcremote-server/images/remote48x.png | Bin 0 -> 6783 bytes pcremote-server/pcremote-server.py | 95 +++++++ pcremote-server/players/amarok.py | 242 ++++++++++++++++ pcremote-server/players/playlist.py | 161 +++++++++++ pcremote-server/players/plistparser.py | 72 +++++ pcremote-server/players/run-amarok.py | 43 +++ pcremote-server/runserver.py | 108 +++++++ pcremote-server/services/ObjectServers.py | 294 ++++++++++++++++++++ pcremote-server/services/ServerHandlers.py | 201 +++++++++++++ pcremote-server/services/service.py | 90 ++++++ pcremote-server/utils/labels.py | 75 +++++ pcremote-server/utils/messages.py | 37 +++ pcremote-server/utils/plistparser.py | 72 +++++ 22 files changed, 2009 insertions(+) create mode 100755 pcremote-server/connection/__init__.py create mode 100755 pcremote-server/connection/bluetoothconnectionmanager.py create mode 100755 pcremote-server/connection/genericconnectionmanager.py create mode 100755 pcremote-server/connection/iconnection.py create mode 100755 pcremote-server/connection/wirelessconnectionmanager.py create mode 100755 pcremote-server/exceptions/__init__.py create mode 100755 pcremote-server/exceptions/exception.py create mode 100755 pcremote-server/images/28x.png create mode 100755 pcremote-server/images/64x.png create mode 100755 pcremote-server/images/PCR_off.bmp create mode 100755 pcremote-server/images/PCR_on.bmp create mode 100755 pcremote-server/images/remote48x.png create mode 100755 pcremote-server/pcremote-server.py create mode 100755 pcremote-server/players/__init__.py create mode 100755 pcremote-server/players/amarok.py create mode 100755 pcremote-server/players/playlist.py create mode 100755 pcremote-server/players/plistparser.py create mode 100755 pcremote-server/players/run-amarok.py create mode 100644 pcremote-server/runserver.py create mode 100755 pcremote-server/services/ObjectServers.py create mode 100755 pcremote-server/services/ServerHandlers.py create mode 100755 pcremote-server/services/__init__.py create mode 100755 pcremote-server/services/service.py create mode 100755 pcremote-server/utils/__init__.py create mode 100755 pcremote-server/utils/labels.py create mode 100755 pcremote-server/utils/messages.py create mode 100755 pcremote-server/utils/plistparser.py diff --git a/pcremote-server/connection/__init__.py b/pcremote-server/connection/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/pcremote-server/connection/bluetoothconnectionmanager.py b/pcremote-server/connection/bluetoothconnectionmanager.py new file mode 100755 index 0000000..581b0f8 --- /dev/null +++ b/pcremote-server/connection/bluetoothconnectionmanager.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Jônatas Isvi +# Email : jonatas.nona@gmail.com +# Reviewer : +# Email : +# Version : 1.0 +# Package : connection +# Description : BluetoothConnectionManager +# ============================================================================ + +import bluetooth +from exceptions import * +from genericconnectionmanager import * + +class BluetoothConnectionManager(GenericConnectionManager): + + """ BluetoothConnectionManager + + manages objects and operations for bluetooth connection. + Subclass of GerericConnectionManager. + """ + + def __init__(self): + GenericConnectionManager.__init__(self) + self.sock = None + self.port = None + self.address = None + self.client_sock = None + self.client_address = None + + # fast way to create a simple server + def create_server(self, protocol, port): + self.create_socket(protocol) + self.set_port(port) + self.bind() + self.listen() + self.accept() + + # fast way to create a simple client + def create_client(self, protocol, address, port): + self.create_socket(protocol) + self.set_address(address) + self.set_port(port) + self.connect() + + # search for all devices + def find_devices(self, time=8): + list_devices = bluetooth.discover_devices(lookup_names = True, duration=time) + if list_devices: + print list_devices + return list_devices + else: + raise BluetoothConnectionError, "Device were not found." + + # search the device port + def find_port(self, addr): + port = None + aux = addr.split(":") + if len(aux) == 6: + services = bluetooth.find_service(address=addr) + for i in range(len(services)): + port = services[i]['port'] + + if port != None: + return port + else: + raise BluetoothConnectionError, "Port not found." + + else: + raise BluetoothConnectionError, "Invalid address." + + # search device services + def find_services(self, service=None, addr=None): + if service == None and addr == None: + list = bluetooth.find_service() + return list + elif service != None and addr == None: + list = bluetooth.find_service(name=service) + if list != []: + return list + else: + raise BluetoothConnectionError, "Name of the service does not exist." + elif service == None and addr != None: + number = addr.split(":") + if(len(number) == 6): + list = bluetooth.find_service(address=addr) + if list != []: + return list + else: + raise BluetoothConnectionError, "Services not found." + else: + raise BluetoothConnectionError, "Invalid address." + elif service != None and addr != None: + number = addr.split(":") + if(len(number) == 6): + list = bluetooth.find_service(name=service, address=addr) + if list != []: + return list + else: + raise BluetoothConnectionError, "Services not found." + else: + raise BluetoothConnectionError, "Invalid address." + + + # search the device indicated by name + def find_device_address_by_name(self, device_name): + list = bluetooth.discover_devices() + addr = None + + for address in list: + if device_name == bluetooth.lookup_name(address): + addr = address + break + if addr: + return addr + else: + raise BluetoothConnectionError, "Device name not found." + + # search only device names + def find_devices_only_names(self): + list = self.find_devices() + list_names = [] + for address, names in list: + list_names += [names] + + if list_names: + return list_names + else: + raise BluetoothConnectionError, "Devices were not found." + + # get the client address + def get_client_address(self): + return self.client_address + + # set the port to communicate + def set_port(self, port): + self.port = port + + # get the port to communicate + def get_port(self): + return self.port + + # set the device address + def set_address(self, address): + aux = address.split(":") + if len(aux) == 6: + self.address = address + else: + raise BluetoothConnectionError, "Invalid address." + + # get the device address + def get_address(self): + return self.address + + # create a socket with a determinated protocol + def create_socket(self, protocol=None): + if protocol == 'rfcomm' or protocol == 'RFCOMM': + self.sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM) + elif protocol == 'l2cap' or protocol == 'L2CAP': + self.sock = bluetooth.BluetoothSocket(bluetooth.L2CAP) + else: + raise BluetoothConnectionError, "Undefined Protocol." + + # bind the communication + def bind(self): + self.sock.bind(("", self.get_port())) + + # just listen the tube, only to server + def listen(self): + self.sock.listen(1) + + # accept the client communication + def accept(self): + self.client_sock, self.client_address = self.sock.accept() + + # connect devices + def connect(self): + self.sock.connect((self.get_address(), self.get_port())) + + # send string message + def send_message(self, msg=None): + self.sock.send(msg) + + # receive string message + def received_message(self): + return self.client_sock.recv(1024) + + # close connection + def close(self): + if self.sock != None and self.client_sock != None: + self.client_sock.close() + self.sock.close() + elif self.sock != None and self.client_sock == None: + self.sock.close() + else: + self.client_sock.close() + diff --git a/pcremote-server/connection/genericconnectionmanager.py b/pcremote-server/connection/genericconnectionmanager.py new file mode 100755 index 0000000..30a2894 --- /dev/null +++ b/pcremote-server/connection/genericconnectionmanager.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : André Portela +# Email : andre_portela@hotmail.com +# Reviewer : Jônatas Isvi +# Email : jonatas.nona@gmail.com +# Version : 1.0 +# Package : connection +# Description : GenericConnectionManager +# ============================================================================ + + +class GenericConnectionManager: + + """ GenericConnectionManager + Superclass of connections + """ + + def __init__(self): + print "GenericConnectionManager iniciado." + self.tipo = "generico" + + # current service running + def identify_app(self): + print "identify_app" diff --git a/pcremote-server/connection/iconnection.py b/pcremote-server/connection/iconnection.py new file mode 100755 index 0000000..310d175 --- /dev/null +++ b/pcremote-server/connection/iconnection.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : André Portela +# Email : andre_portela_@hotmail.com +# Reviewer : Jônatas Isvi +# Email : jonatas.nona@gmail.com +# Version : 1.0 +# Package : connection +# Description : Iconnection Interface Class +# ============================================================================ + +from wirelessconnectionmanager import * +from bluetoothconnectionmanager import * +from exceptions import * + +# connections aliases +_btconst = ['bluetooth', 'BLUETOOTH', 'blue'] +_wificonst = ['wireless', 'WIRELESS', 'wifi'] + +class Iconnection: + + """ Iconnection + Interface for wireless and bluetooth connections. + Manages all commonalities operations between entities. + """ + def __init__(self, string): + self.string = string + if(self.string in _btconst): + self.obj = BluetoothConnectionManager() + elif(self.string in _wificonst): + self.obj = WirelessConnectionManager() + else: + raise IconnectionError, "Undefined type." + + + # +---------------------------------------------+ + # | Generic methods -> Wireless and Bluetooth | + # +---------------------------------------------+ + + # create a socket with defined protocol + def create_socket(self, protocol=None): + self.obj.create_socket(protocol) + + # connect device + def connect(self): + self.obj.connect() + + # accept the connection + def accept(self): + return self.obj.accept() + + # send a message to device + def send_message(self, msg=None): + self.obj.send_message(msg) + + # received a message + def received_message(self): + return self.obj.received_message() + + # bind the connection + def bind(self): + self.obj.bind() + + # listen the connection + def listen(self): + self.obj.listen() + + # close connection + def close(self): + self.obj.close() + + # set the port to communicate + def set_port(self, port): + self.obj.set_port(port) + + # get the port to communicate + def get_port(self): + return self.obj.get_port() + + # set the device address + def set_address(self, address): + self.obj.set_address(address) + + # get the device address + def get_address(self): + return self.obj.get_address() + + # get the client address + def get_client_address(self): + return self.obj.get_client_address() + + # +------------------------------------------+ + # | Bluetooth: particular behaviors | + # +------------------------------------------+ + + # fast way to create a simple server + def bluetooth_create_server(self, protocol, port): + if self.string in _btconst: + return self.obj.create_server(protocol, port) + else: + raise IconnectionError, "Only method used by Bluetooth connections." + + # fast way to create a simple client + def bluetooth_create_client(self, protocol, address, port): + if self.string in _btconst: + return self.obj.create_client(protocol, address, port) + else: + raise IconnectionError, "Only method used by Bluetooth connections." + + # search for all devices + def bluetooth_find_devices(self, time=8): + if self.string in _btconst: + return self.obj.find_devices(time) + else: + raise IconnectionError, "Only method used by Bluetooth connections." + + # search only devices names + def bluetooth_find_devices_only_names(self): + if self.string in _btconst: + return self.obj.find_devices_only_names() + else: + raise IconnectionError, "Only method used by Bluetooth connections." + + # search the device port + def bluetooth_find_port(self, addr): + if self.string in _btconst: + return self.obj.find_port(addr) + else: + raise IconnectionError, "Only method used by Bluetooth connections." + + # search device services + def bluetooth_find_services(self, service=None, addr=None): + if self.string in _btconst: + return self.obj.find_services(service, addr) + else: + raise IconnectionError, "Only method used by Bluetooth connections." + + + # search the device indicated by name + def bluetooth_find_device_address_by_name(self, device_name=None): + if self.string in _btconst: + return self.obj.find_device_address_by_name(device_name) + else: + raise IconnectionError, "Only method used by Bluetooth connections." + + + + # +---------------------------------+ + # | Wireless: particular behaviors | + # +---------------------------------+ diff --git a/pcremote-server/connection/wirelessconnectionmanager.py b/pcremote-server/connection/wirelessconnectionmanager.py new file mode 100755 index 0000000..e92ec3d --- /dev/null +++ b/pcremote-server/connection/wirelessconnectionmanager.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : André Portela +# Email : andre_portela_@hotmail.com +# Reviewer : Jônatas Isvi +# Email : jonatas.nona@gmail.com +# Version : 0.1 +# Package : connection +# Description : Wireless Connection Manager Class +# ============================================================================ + +from genericconnectionmanager import * + +class WirelessConnectionManager(GenericConnectionManager): + + """ WirelessConnectionManager + Manages objects and operations for wireless connection. + Subclass of GenericConnectionManager. + """ + + def __init__(self): + GenericConnectionManager.__init__(self) + self.tipo = "wireless" + + + diff --git a/pcremote-server/exceptions/__init__.py b/pcremote-server/exceptions/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/pcremote-server/exceptions/exception.py b/pcremote-server/exceptions/exception.py new file mode 100755 index 0000000..697535b --- /dev/null +++ b/pcremote-server/exceptions/exception.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Jônatas Isvi +# Email : jonatas.nona@gmail.com +# Reviewer : +# Email : +# Version : 1.0 +# package : exceptions +# Description : Exceptions +# ============================================================================ + +class BluetoothConnectionError(Exception): + ''' Treatment of errors bluetooth connections ''' + pass + +class WirelessConnectionError(Exception): + ''' Treatment of errors wireless connections ''' + pass + +class IconnectionError(Exception): + ''' Treatment of errors Iconnection class ''' + pass + + + + + + + + + + + + + diff --git a/pcremote-server/images/28x.png b/pcremote-server/images/28x.png new file mode 100755 index 0000000000000000000000000000000000000000..9c918db0fba3e3362dd81ee0a205baadfed9f5a6 GIT binary patch literal 4474 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 M07*qoM6N<$g0#7FcK`qY literal 0 HcmV?d00001 diff --git a/pcremote-server/images/64x.png b/pcremote-server/images/64x.png new file mode 100755 index 0000000000000000000000000000000000000000..2e13bd785ac71943556a5c2e1f292f0820913dee GIT binary patch literal 9438 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 literal 0 HcmV?d00001 diff --git a/pcremote-server/images/PCR_off.bmp b/pcremote-server/images/PCR_off.bmp new file mode 100755 index 0000000000000000000000000000000000000000..c686d433c7a41e6ae6a15f587b40f38582c14641 GIT binary patch literal 1080 zcmb7DyJ})l6kX#hKBG~h_=2$rs02iXFoKODXk(>BI}5Q0K@h=jNU4QjDOmUg_E8(L zu-7&q>JNx$;oNm*j$~$pnGx1K=iYtR-g~XHP3OO9AbwBEGpNA!6JPLE*#RmgZa4ja z_@F$F$0I&IKCoCU{tM*n_j_zM8#EdX+}_^8VzIzvGC{A`L#x$7tyV*&Qe8#4T*i96 z#%i^K%jLq|-5t*7vp^safXCwzdxOCMjYcEDIQKH|d_Ko=xkMt75N90@2i$JA*hiyL zvHJafk^AQ6M!f5Ex=U~G@9%=2N~J`M{oCz!v441Y5G!l4pZoj!%e&Mj`RR08#HgG4 z$&u&d@i-!p2xLCi_?@52Y=}^ z&ohsYkMQ|?qEE@M)oP#zPNx%Qv-y%k>ap2uLXX$$1@#1jLD1{*cr5&TeSJltP(U~w zR$?_}s?Jxz81ILCu9L~+!he2#MlzYa%KMy;_ru!6XMU+v68yi_=l#ezmHjuHO;jos z;Q@OfI8)TaIkZ}>Vy5XUJ)yT;$>;obyIr)~ZM0e~)a&(MP_0%$ZTWm2*=!b>Oa`1u z&f{!0!)~|3bUMXoG{SH=#9%P^1O0v2~3K^LcpIusnlAp}7J(*6TS9W0JQ27iIO zk0m&`n>8Tn9}v;OdT#qkN`M0u)BgfF>-8Fo#R9cj4VRafxVX50(P+fk*%`E2E!1i?R4Ua;6pKa7 z=X1QizQW;f;QIO+R;v~J{a)a5xnQwa#MfvvAQ+=n@=hib%w{vhVlnu9K8ZV>PJ}`s z@%?_kSmxI2_2TPvx?^vH!9e(lL;`_8K;o>)epo9KiHN{oz0b#aSetmyFBA&G|66^|qui;| z|I^bGN~Mx$z#a(R6mNoecy)CpH%(os3ALpqpZD8tx6y1iQLoogtyX_Sxm*Tk%Vx7k zr_)HKQs54Gk1sDTSglqVkH_fu`{?z0cz%BV1Kn;HolXa>Rtt?r1I+pG@POrViQR4| SIoJn(3;zmoH=E7jAK(Ws4Nsu} literal 0 HcmV?d00001 diff --git a/pcremote-server/images/remote48x.png b/pcremote-server/images/remote48x.png new file mode 100755 index 0000000000000000000000000000000000000000..22b56402f7c0937af19a254c7b048ccdbe05eff0 GIT binary patch literal 6783 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 literal 0 HcmV?d00001 diff --git a/pcremote-server/pcremote-server.py b/pcremote-server/pcremote-server.py new file mode 100755 index 0000000..195af3e --- /dev/null +++ b/pcremote-server/pcremote-server.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python + +from runserver import Server +import gtk +import thread +import sys + +class Service: + + def start_server(self, widget): + + if self.connected == False: + imagepath = self.images.replace('pcremote-server.py','images/PCR_on.bmp') + self.staticon.set_from_file(imagepath) + self.staticon.set_tooltip("PC Remote Server - Online") + + img = gtk.Image() + img.set_from_stock(gtk.STOCK_DISCONNECT, gtk.ICON_SIZE_MENU) + self.menuItemCon.set_image(img) + + self.srv = Server("PC Remote") + thread.start_new_thread(Server.start,(self.srv,"server")) + + else: + imagepath = self.images.replace('pcremote-server.py','images/PCR_off.bmp') + self.staticon.set_from_file(imagepath) + self.staticon.set_tooltip("PC Remote Server - Offline") + + img = gtk.Image() + img.set_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) + + self.menuItemCon.set_image(img) + + thread.exit_thread() + + self.connected = not self.connected + + def destroyer(self, widget,response_id, data= None): + if response_id == gtk.RESPONSE_OK: + gtk.main_quit() + else: + widget.hide() + + def popup(self, widget): + dialog = gtk.MessageDialog( + parent = None, + flags = gtk.DIALOG_DESTROY_WITH_PARENT, + type = gtk.MESSAGE_INFO, + buttons = gtk.BUTTONS_OK_CANCEL, + message_format = "Do you want to shut down the server?") + dialog.set_title('PC Remote Server') + dialog.connect('response', self.destroyer) + dialog.show() + + def popup_menu_cb(self, widget, button, time, data = None): + if button == 3: + if data: + data.show_all() + data.popup(None, None, None, 3, time) + + + def __init__(self): + + self.images = sys.argv[0] + self.connected = False + + self.staticon = gtk.StatusIcon() + imagepath = self.images.replace('pcremote-server.py','images/PCR_off.bmp') + self.staticon.set_from_file(imagepath) + self.staticon.set_tooltip("PC Remote Server(offline)") + + self.menu = gtk.Menu() + + self.menuItemCon = gtk.ImageMenuItem(gtk.STOCK_EXECUTE) + self.menuItemCon.connect('activate', self.start_server) + + self.menuItemExit = gtk.ImageMenuItem(gtk.STOCK_QUIT) + self.menuItemExit.connect('activate', self.popup) + + self.menu.append(self.menuItemCon) + self.menu.append(self.menuItemExit) + + self.staticon.connect('popup-menu', self.popup_menu_cb, self.menu) + + self.staticon.set_visible(True) + + gtk.gdk.threads_init() + gtk.gdk.threads_enter() + + gtk.main() + + gtk.gdk.threads_leave() + +print sys.argv +Srv = Service() diff --git a/pcremote-server/players/__init__.py b/pcremote-server/players/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/pcremote-server/players/amarok.py b/pcremote-server/players/amarok.py new file mode 100755 index 0000000..2d54fb0 --- /dev/null +++ b/pcremote-server/players/amarok.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Jonatas Isvi +# Email : jonatas.nona@gmail.com +# Reviewer : +# Email : +# Version : 1.0 +# Packge : players +# Description : Amarok Player +# ============================================================================ + +import os +import commands +import random +from playlist import Playlist +import pydcop + +# command line +def shell(command): + return commands.getoutput(command) + + +# starts the amarok player application +def start(): + os.popen('amarok') + + +# close the amarok player application +def shutdown(): + shell('dcop amarok player stop') + pid = shell('pidof amarokapp') + shell('kill -9 %s' % pid) + + +# verifies if the amarok is running +def isRunning(): + pid = shell('pidof amarokapp') + if pid > 0: + return True + else: + return False + +def send_file(addr, path): + shell("bluetooth-sendto --dest=%s %s" + (addr, path)) + +class AmarokPlayer(): + + """ Amarok + Define all states and functions of amarok player. + This class will build to support PCRemote Player, + receiving messages from any devices with a bluetooth + connection. + """ + + # some importants variables + def __init__(self): + self.amarok = pydcop.anyAppCalled("amarok") + self.playlist = Playlist(self.amarok.playlist.saveCurrentPlaylist()) + self.isPlaying() + + # refresh playlist, accessing the Playlist class instance + def refresh_playlist(self): + self.playlist = Playlist(self.amarok.playlist.saveCurrentPlaylist()) + self.isPlaying() + + # get all songs of playlist + def song_list(self): + self.playlist.show() + + # show current song, acessing the Playlist class instance + def current_song(self): + self.isPlaying() + + # verifies if this amarok app is running + def isRunning(self): + aux = pydcop.anyAppCalled("amarok") + if aux: + return aux + else: + return None + + # verifies if this amarok app is playing and update the + # Playlist with current song + def isPlaying(self): + if not self.amarok.player.isPlaying() == 'true': + self.playlist.update(self.amarok.playlist.getActiveIndex(),\ + self.amarok.player.title(), \ + self.amarok.player.artist(), \ + self.amarok.player.path(), \ + "." + self.amarok.player.type(), \ + ) + return True + else: + return False + + # get the players name + def getName(self): + return "Amarok" + + # send audio files to the N810 device + def file_properties(self, index=None): + track = self.amarok.playlist.getActiveIndex() + audiofile = self.playlist.song_properties(index=track, path=True) + #audiofile = (self.playlist.song_filename(index),\ + # self.playlist.song_size(index)) + return audiofile + + # next button and sets current song + def next(self): + self.amarok.player.next() + self.playlist.update(self.amarok.playlist.getActiveIndex(),\ + self.amarok.player.title(), \ + self.amarok.player.artist(), \ + self.amarok.player.path(), \ + "." + self.amarok.player.type(), \ + ) + + # prev button and sets current song + def prev(self): + self.amarok.player.prev() + self.playlist.update(self.amarok.playlist.getActiveIndex(),\ + self.amarok.player.title(), \ + self.amarok.player.artist(), \ + self.amarok.player.path(), \ + "." + self.amarok.player.type(), \ + ) + + # play button and sets current song + #def play(self): + #self.amarok.player.play() + #self.playlist.update(self.amarok.playlist.getActiveIndex() + 1,\ + # self.amarok.player.title(), \ + # self.amarok.player.artist(), \ + # self.amarok.player.path(), \ + # "." + self.amarok.player.type(), \ + # ) + + # play button and sets current song + # receive track or random form + # the argument track has intended to manipulate + # the playlist form when the user indicate a song track + # in client application + def play(self, track=-1, rdm=False): + if rdm: + index = random.randint(0, self.playlist.length() - 1) + self.amarok.playlist.playByIndex(index) + self.playlist.update(index + 1, \ + self.amarok.player.title(), \ + self.amarok.player.artist(), \ + self.amarok.player.path(), \ + "." + self.amarok.player.type(),\ + ) + elif track != -1: + self.amarok.playlist.playByIndex(track) + self.playlist.update(track-1, \ + self.amarok.player.title(), \ + self.amarok.player.artist(), \ + self.amarok.player.path(), \ + "." + self.amarok.player.type(),\ + ) + else: + self.amarok.player.play() + self.playlist.update(self.amarok.playlist.getActiveIndex() + 1,\ + self.amarok.player.title(), \ + self.amarok.player.artist(), \ + self.amarok.player.path(), \ + "." + self.amarok.player.type(), \ + ) + + # play button with index song and sets current song + #def play_track(self, index): + # self.amarok.playlist.playByIndex(index-1) + # self.playlist.update(index, \ + # self.amarok.player.title(), \ + # self.amarok.player.artist(), \ + # self.amarok.player.path(), \ + # "." + self.amarok.player.type(),\ + # ) + + # random play songs + #def play_random(self): + # index = random.randint(0, self.playlist.length() - 1) + # self.amarok.playlist.playByIndex(index) + # self.playlist.update(index+1, \ + # self.amarok.player.title(), \ + # self.amarok.player.artist(), \ + # self.amarok.player.path(), \ + # "." + self.amarok.player.type(),\ + # ) + + # pause button + def pause(self): + self.amarok.player.pause() + + # mute button + def mute(self): + self.amarok.player.mute() + + # stop button + def stop(self): + self.amarok.player.stop() + + # get the current volume value + def get_volume(self): + return self.amarok.player.getVolume() + + # set up volume + def volume_up(self, increase=1): + if (self.get_volume() + increase) <= 100: + up = self.get_volume() + increase + self.amarok.player.setVolume(up) + else: + print "erro!" + + # set down volume + def volume_down(self, decrement=1): + if (self.get_volume() - decrement) >= 0: + down = self.get_volume() - decrement + self.amarok.player.setVolume(down) + else: + print "erro!" + + # set seek value + def seek(self, value): + self.amarok.player.seek(value) diff --git a/pcremote-server/players/playlist.py b/pcremote-server/players/playlist.py new file mode 100755 index 0000000..31a3a61 --- /dev/null +++ b/pcremote-server/players/playlist.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Jonatas Isvi +# Email : jonatas.nona@gmail.com +# Reviewer : +# Email : +# Version : 1.0 +# Package : players +# Description : Playlist +# ============================================================================ + +import plistparser +import pydcop + +class Playlist(): + + """ Playlist + make the interpreter and manipulation + of the player playlist, creates a composite + with any player class. + """ + + # some importants variables + # analyze if file is a playlist + def __init__(self, file): + if self.isPlaylist(file): + self.file = file + self.songs = self.load() + self.currentSong = 0 + self.fix() + else: + raise("Argument is not a playlist file") + + # analyzes the file + def isPlaylist(self, file): + if not file: + return False + else: + return True + + # make a list of dicts songs + def load(self): + self.songs = plistparser._request(self.file) + return self.songs + + # get the length of the current playlist + def length(self): + return len(self.songs) + + # update the current song in songs list and return a song dict + def update(self, track, title, artist, path, ext): + self.currentSong = track + if self.songs[self.currentSong]['title'] == 'Unknown Title': + self.songs[self.currentSong]['title'] = title + if self.songs[self.currentSong]['artist'] == 'Unknown Artist': + self.songs[self.currentSong]['artist'] = artist + self.songs[self.currentSong]['path'] = path + self.songs[self.currentSong]['extension'] = ext + print self.songs[self.currentSong] + + + # show the current song + def show_playing_now(self): + return ('TITLE: %s' % self.songs[self.currentSong]['title'], \ + 'ARTIST: %s' % self.songs[self.currentSong]['artist'],\ + 'TRACK: %s' % self.songs[self.currentSong]['track'] + ) + + # get the current song filename if index is None + def song_filename(self, index=None): + if index == None: + return self.songs[self.currentSong]['title'] +" - "+\ + self.songs[self.currentSong]['artist'] + \ + self.songs[self.currentSong]['extension'] + + else: + return self.songs[index]['title'] +" - "+\ + self.songs[index]['artist'] + \ + self.songs[index]['extension'] + + # get thr current song filesize if index is None + def song_size(self, index=None): + if index == None: + return int(self.songs[self.currentSong]['filesize']) + else: + return int(self.songs[index]['filesize']) + + # show all songs of the playlist + def show(self): + for i in range(self.length()): + print self.songs[i]['track'], " - ", \ + self.songs[i]['title'], " | ", \ + self.songs[i]['artist'], \ + "\n" + + # fix some problems of musics tags + def fix(self): + for i in range(self.length()): + if self.songs[i]['title'] == None: + self.songs[i]['title'] = 'Unknown Title' + elif self.songs[i]['artist'] == None: + self.songs[i]['artist'] = 'Unknown Artist' + + + # get the porperties of any song of ther playlist + def song_properties(self, index=None, track=False, title=False,\ + artist=False, ext=False, filesize=False, \ + duration=False, path=False): + props = {} + if index == None: + if track: + props['track'] = self.songs[self.currentSong]['track'] + if title: + props['title'] = self.songs[self.currentSong]['title'] + if artist: + props['artist'] = self.songs[self.currentSong]['artist'] + if ext: + props['ext'] = self.songs[self.currentSong]['extension'] + if filesize: + props['filesize'] = self.songs[self.currentSong]['filesize'] + if duration: + props['duration'] = self.songs[self.currentSong]['duration'] + if path: + props['path'] = self.songs[self.currentSong]['path'] + + return props + else: + if track: + props['track'] = self.songs[index]['track'] + if title: + props['title'] = self.songs[index]['title'] + if artist: + props['artist'] = self.songs[index]['artist'] + if ext: + props['ext'] = self.songs[index]['extension'] + if filesize: + props['filesize'] = self.songs[index]['filesize'] + if duration: + props['duration'] = self.songs[index]['duration'] + if path: + props['path'] = self.songs[index]['path'] + + return props + diff --git a/pcremote-server/players/plistparser.py b/pcremote-server/players/plistparser.py new file mode 100755 index 0000000..faaa0ac --- /dev/null +++ b/pcremote-server/players/plistparser.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Jonatas Isvi +# Email : jonatas.nona@gmail.com +# Reviewer : +# Email : +# Version : 1.0 +# Package : utils +# Description : plisparser +# ============================================================================ + +from xml.etree import cElementTree as ElementTree + +# get the file +def _request(url): + xml = url + return parse_playlist_file(xml) + +# parser the file +def parse_playlist_file(xml): + tree = ElementTree.parse(xml) + listsongs = [] + dictsongs = {} + count = duration = filesize = 0 + title = artist = path = '' + + for child in tree.getiterator(): + if child.tag == 'Title': + title = child.text + elif child.tag == 'Artist': + artist = child.text + elif child.tag == 'Length': + duration = child.text + elif child.tag == 'Filesize': + filesize = child.text + count = count + 1 + dictsongs = {'track' : count, + 'title' : title, + 'artist' : artist, + 'duration' : duration, + 'filesize' : filesize, + 'path' : None, + 'extension' : None, + } + listsongs.append(dictsongs) + + return listsongs + + + + + + + + diff --git a/pcremote-server/players/run-amarok.py b/pcremote-server/players/run-amarok.py new file mode 100755 index 0000000..2d8e9bf --- /dev/null +++ b/pcremote-server/players/run-amarok.py @@ -0,0 +1,43 @@ +import amarok + +if not amarok.isRunning(): + player = amarok.AmarokPlayer() +else: + print "entrou aqui" + amarok.start() + player = amarok.AmarokPlayer() + +while(1): + data = raw_input(">>> ") + if data == '#exit' or data == '#quit' or data == '#close': + print "Application closed." + amarok.shutdown() + break + elif data == 'next': + player.next() + elif data == 'prev': + player.prev() + elif data == 'play': + player.play() + elif data == 'pause': + player.pause() + elif data == 'stop': + player.stop() + elif data == 'mute': + player.mute() + elif data == 'volume-up': + player.volume_up() + elif data == 'volume-down': + player.volume_down() + elif data == 'current_song': + print player.current_song() + elif data == 'random': + player.play_random() + elif data == 'play-track': + index = input('track: ') + player.play_track(index) + elif data == 'refresh': + player.refresh_playlist() + elif data == 'show': + player.song_list() + diff --git a/pcremote-server/runserver.py b/pcremote-server/runserver.py new file mode 100644 index 0000000..b91efd2 --- /dev/null +++ b/pcremote-server/runserver.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Nilson ; Jonatas Izvi; Andre Portela +# Email : fergus.mao@gmail.com ; nona@gmail.com ; +# andre_portela_@hotmail.com; +# Version : 1.0 +# Class : Server File - This is the main script of the server +# ============================================================================ + +from connection.iconnection import * +from services.service import * +from utils import * +from utils.messages import * + +class Server(): + + def __init__(self, AppName): + self.msgbox = Message(AppName) + self.msgbox.show_message("Server Initialized...") + + def start(self, servername): + + label = Labels() + iconn = Iconnection('blue') + iconn.bluetooth_create_server('l2cap', 0x1001) + + address = iconn.get_client_address() + + self.msgbox.show_message("Accepted connection from " + address[0]) + + while (1): + + data = iconn.received_message() + + if data == 'Tablet:#start': + + self.msgbox.show_message('Service Tablet initialized...') + + service = Service() + service.set_service('Tablet') + + while(1): + data = iconn.received_message() + if data == 'Tablet:#stop': + service.clean_all() + self.msgbox.show_message('Service Tablet stoped') + break + service.execute(data) + + elif data == 'Slideshow:#start': + + self.msgbox.show_message('Service Slideshow initialized...') + + service = Service() + service.set_service('Slideshow') + + while(1): + data = iconn.received_message() + if data == 'Slideshow:#stop': + service.clean_all() + self.msgbox.show_message('Service Slideshow stoped') + break + print data, "\n" + service.execute(data) + + elif data == 'Player:#start': + + self.msgbox.show_message('Service Player initialized...') + + service = Service() + service.set_service('Player') + + while(1): + data = iconn.received_message() + if data == 'Player:#stop': + self.msgbox.show_message('Service Player stoped') + break + elif data == 'Player:#download': + service.set_address_to_download(address[0]) + elif data == 'Player:#load_playlist': + # e preciso criar um metodo de transferencia + # no caso de carregar uma playlist para o cliente + service.execute_transfer(data) + + service.execute(data) + + else: + iconn.close() + self.msgbox.show_message('Desconected from ' + address[0]) + break diff --git a/pcremote-server/services/ObjectServers.py b/pcremote-server/services/ObjectServers.py new file mode 100755 index 0000000..dfbe76c --- /dev/null +++ b/pcremote-server/services/ObjectServers.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Nilson Silva, Jonatas Isvi, Andre Portela +# Email : fergus.mao@gmail.com, jonatas.nona@gmail.com, +# andre_portela_@hotmail.com +# Reviewer : Jônatas Isvi +# Email : +# Version : 1.0 +# Package : services +# Description : Mouse Server, Keyboard Server +# ============================================================================ + +import time +from utils.labels import * +from ServerHandlers import * +from players import amarok + +class Mouse_Server: + + """ Mouse Server + Defines all mouse behaviors. + Clicks and coordinates. + """ + + #Initialize the class + def __init__(self, service): + self._service_name = service + self.mouse = Mouse() + self.labels = Labels() + self.timer = time + self.timerclick = 0 + + self.fg_dbclick = False + self.fg_move = True + self.x = 0 + self.y = 0 + + #Executes the action requested by the Service Manager + def execute(self, command): + + self.mouse_counter_lclick() + + if (command == self.labels.CLICK): + self.mouse_click() + elif (command == self.labels.DOUBLE_CLICK): + self.mouse_press_dbclick() + elif (command == self.labels.TRIPLE_CLICK): + self.mouse_release_dbclick() + elif (command == self.labels.LEFT_CLICK): + self.mouse_lclick() + elif (command == self.labels.MIDDLE_CLICK): + self.mouse_mclick() + elif (command == self.labels.RIGHT_CLICK): + self.mouse_rclick() + elif (command[0] == "#"): + self.mouse_fator(command) + else: + self.mouse_move(command) + + #Gets the time the mouse pointer is pressed. If It is greater than (or equal to) 2s, The Mouse Left Click is activated. + def mouse_counter_lclick(self): + + if ((not self.fg_move) and ((int(self.timer.time()) - self.timerclick) >= 2)): + self.mouse.right_click(True) + self.mouse.right_click(False) + self.timerclick = 0 + self.fg_move = True + + #Mouse Pointer - Single Click + def mouse_click(self): + self.timerclick = int(self.timer.time()) + self.fg_move = False + + #Mouse Pointer - Double Click + def mouse_press_dbclick(self): + self.mouse.left_click(True) + self.fg_dbclick = True + + #Mouse Pointer - Released after a Double Click + def mouse_release_dbclick(self): + if self.fg_dbclick: + self.mouse.left_click(False) + self.fg_dbclick = False + + #Mouse Left Click + def mouse_lclick(self): + self.mouse.left_click() + + #Mouse Middle Click + def mouse_mclick(self): + self.mouse.middle_click() + + #Mouse Right Click + def mouse_rclick(self): + self.mouse.right_click() + + #Sets the factor of the Mouse Pointer Move + def mouse_fator(self, command): + num = "" + for i in range(1, len(command)): + num = num + command(i) + + self.mouse.set_fator(int(num)) + + #Moves the Mouse Pointer + def mouse_move(self, command): + coord = command.split(",") + + i = int(coord[0]) - self.x + if ((abs(i) == 1) or (abs(i) >= 20)): + i = 0 + + j = int(coord[1]) - self.y + if ((abs(j) == 1) or (abs(j) >= 20)): + j = 0 + + if not ((i == 0) and (j == 0)): + if ((i >= 4) or (j >= 4)): + self.fg_move = True + if self._service_name == "Tablet": + self.mouse.position(i, j) + else: + self.mouse.position(-j, i) + + self.x = int(coord[0]) + self.y = int(coord[1]) + + def clean_up(self): + self.mouse.clean_up() + +class KeyBoard_Server: + + """ Keyboard Server + Defines all keyboard behaviors. + Map keys and events. + """ + + def __init__(self, service): + self.keyboard = Keyboard() + self.shift_flag = False + self.control_flag = False + self.alt_flag = False + self._service_name = service + + # execute key command + def execute(self, command): + + print "\n", command + + if(command == 'F8'): + self.keyboard.reproduce_key_press('Control_L') + self.keyboard.reproduce_key_press('z') + self.keyboard.reproduce_key_release('z') + self.keyboard.reproduce_key_release('Control_L') + elif(self._service_name == 'Slideshow' and command == 'F6'): + self.keyboard.reproduce_key_press('F5') + self.keyboard.reproduce_key_release('F5') + elif(command == 'Control_R'): + self.control_flag = True + self.keyboard.reproduce_key_press('Control_R') + #self.keys.append(command) + elif(command == 'Shift_L'): + self.shift_flag = True + self.keyboard.reproduce_key_press('Shift_L') + #self.keys.append(command) + elif(command == 'Alt_L'): + self.alt_flag = True + self.keyboard.reproduce_key_press('Alt_L') + elif(command == 'F7'): + self.keyboard.reproduce_key_press('Control_L') + self.keyboard.reproduce_key_press('y') + self.keyboard.reproduce_key_release('y') + self.keyboard.reproduce_key_release('Control_L') + elif(command == 'Alt+F1'): + self.keyboard.reproduce_key_press('Alt_L') + self.keyboard.reproduce_key_press('F1') + self.keyboard.reproduce_key_release('F1') + self.keyboard.reproduce_key_release('Alt_L') + elif(command == 'Alt+F2'): + self.keyboard.reproduce_key_press('Alt_L') + self.keyboard.reproduce_key_press('F2') + self.keyboard.reproduce_key_release('F2') + self.keyboard.reproduce_key_release('Alt_L') + elif(command == 'Alt+F4'): + self.keyboard.reproduce_key_press('Alt_L') + self.keyboard.reproduce_key_press('F4') + self.keyboard.reproduce_key_release('F4') + self.keyboard.reproduce_key_release('Alt_L') + elif(command == 'Alt+F9'): + self.keyboard.reproduce_key_press('Alt_L') + self.keyboard.reproduce_key_press('F9') + self.keyboard.reproduce_key_release('F9') + self.keyboard.reproduce_key_release('Alt_L') + elif(command == 'Alt+F0'): + self.keyboard.reproduce_key_press('Alt_L') + self.keyboard.reproduce_key_press('F10') + self.keyboard.reproduce_key_release('F10') + self.keyboard.reproduce_key_release('Alt_L') + elif(command == 'Alt+Space'): + self.keyboard.reproduce_key_press('Alt_L') + self.keyboard.reproduce_key_press('space') + self.keyboard.reproduce_key_release('space') + self.keyboard.reproduce_key_release('Alt_L') + elif(command == 'Tab'): + self.keyboard.reproduce_key_press('Tab') + self.keyboard.reproduce_key_release('Tab') + else: + self.keyboard.reproduce_key_press(command) + self.keyboard.reproduce_key_release(command) + + if self.shift_flag: + self.keyboard.reproduce_key_release('Shift_L') + #self.keys.remove('Shift_L') + self.shift_flag = False + elif self.control_flag: + self.keyboard.reproduce_key_release('Control_R') + #self.keys.remove('Control_R') + self.control_flag = False + elif self.alt_flag: + self.keyboard.reproduce_key_release('Alt_L') + self.alt_flag = False + + # clean all keys pressed + def clean_up(self): + self.keyboard.clean_up() + +class Player_Server(): + + def __init__(self): + if not amarok.isRunning(): + self.player = amarok.AmarokPlayer() + else: + amarok.start() + self.player = amarok.AmarokPlayer() + self.labels = Labels() + + def execute(self, command): + if len(command) > 2: + if command[1] == self.labels.PLAY: + if command[2] and command[3]: + self.player.play(track=int(command[2]), rmd=bool(command[3])) + elif command[2]: + arg = int(command[2]) + if isinstance(arg, int): + self.player.play(track=arg) + else: + arg = bool(command[2]) + self.player.play(rmd=arg) + else: + pass + else: + if command[1] == self.labels.STOP: + self.player.stop() + elif command[1] == self.labels.PLAY: + self.player.play() + elif command[1] == self.labels.PAUSE: + self.player.pause() + elif command[1] == self.labels.NEXT: + self.player.next() + elif command[1] == self.labels.PREVIOUS: + self.player.prev() + elif command[1] == self.labels.VOL_UP: + self.player.volume_up() + elif command[1] == self.labels.VOL_DOWN: + self.player.volume_down() + elif command[1] == self.labels.SEEK: + self.player.seek(int(command[2])) + elif command[1] == self.labels.DOWNLOAD: + path = self.player.file_properties() + addr = command[2] + amarok.send_file(addr, path) + pass + elif command[1] == self.labels.LOAD_PLAYLIST: + # falta o metodo de trasnferencia + pass + else: + pass diff --git a/pcremote-server/services/ServerHandlers.py b/pcremote-server/services/ServerHandlers.py new file mode 100755 index 0000000..4b16012 --- /dev/null +++ b/pcremote-server/services/ServerHandlers.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Nilson Silva +# Email : fergus.mao@gmail.com +# Reviewer : Jônatas Isvi +# Email : jonatas.nona@gmail.com +# Version : 1.0 +# Package : service +# Description : Singleton, Mouse and Keyboard +# ============================================================================ + +import Xlib +from Xlib import display, X, XK + +class Singleton_Xlib(): + + """ Singleton + defines a singleton. + """ + def __init__(self): + self.display = display.Display() + self.screen = self.display.screen() + +xlib_srv = Singleton_Xlib() + +class Mouse(object): + + """ Mouse + pass mouse information to Xorg + """ + + #Initialize the class + def __init__(self): + self.disp = xlib_srv.display + self.screen = xlib_srv.screen + self.fator = 10 + self.lbutton = False + self.mbutton = False + self.rbutton = False + self.buttons = [] + + #Set the mouse pointer position + def position(self,x=None,y=None): + + if (x == None): + x = self.fator + + if (y == None): + y = self.fator + + #Get the current mouse pointer position + current_x = self.screen.root.query_pointer()._data["root_x"] + current_y = self.screen.root.query_pointer()._data["root_y"] + + def absolute(ax = None, ay = None): + if (ax == None): + ax = x + if (ay == None): + ay = y + + self.screen.root.warp_pointer(ax, ay) + self.disp.sync() + + def relative(): + rX = current_x + x + rY = current_y + y + absolute(rX,rY) + + relative() + + #Returns the current X position + def get_x(self): + return self.screen.root.query_pointer()._data["root_x"] + + #Returns the current Y position + def get_y(self): + return self.screen.root.query_pointer()._data["root_y"] + + #Defines the factor(px) of the mouse pointer move + def set_fator(self,fator): + self.fator = fator + + #Returns the factor + def get_fator(self): + return self.fator + + #Mouse Left Click + def left_click(self, fg_lbutton = None): + + if (fg_lbutton != None): + self.lbutton = not fg_lbutton + + if not self.lbutton: + self.disp.xtest_fake_input(X.ButtonPress, 1, 0) + self.buttons.append('left_button') + self.lbutton = True + else: + self.disp.xtest_fake_input(X.ButtonRelease, 1, 5) + self.buttons.remove('left_button') + self.lbutton = False + + self.disp.flush() + + #Mouse Middle Click + def middle_click(self): + if not self.mbutton: + self.disp.xtest_fake_input(X.ButtonPress, 2, 0) + self.buttons.append('middle_button') + self.mbutton = True + else: + self.disp.xtest_fake_input(X.ButtonRelease, 2, 5) + self.buttons.remove('middle_button') + self.mbutton = False + + self.disp.flush() + + #Mouse Right Click + def right_click(self, fg_rbutton = None): + + if (fg_rbutton != None): + self.rbutton = not fg_rbutton + + if not self.rbutton: + self.disp.xtest_fake_input(X.ButtonPress, 3, 0) + self.buttons.append('right_button') + self.rbutton = True + else: + self.disp.xtest_fake_input(X.ButtonRelease, 3, 5) + self.buttons.remove('right_button') + self.rbutton = False + + self.disp.flush() + + def clean_up(self): + if self.buttons: + while self.buttons: + button = self.buttons.pop() + if button == 'left_button': + self.disp.xtest_fake_input(X.ButtonRelease, 1, 5) + self.disp.xtest_fake_input(X.ButtonPress, 3, 5) + self.disp.xtest_fake_input(X.ButtonRelease, 3, 5) + elif button == 'middle_button': + self.disp.xtest_fake_input(X.ButtonRelease, 2, 5) + elif button == 'right_button': + self.disp.xtest_fake_input(X.ButtonRelease, 3, 5) + + print self.buttons + +class Keyboard(): + + """ Keyboard + pass keyboard information to Xorg + """ + + def __init__(self): + self.display = xlib_srv.display + self.screen = xlib_srv.screen + self.keys = [] + + # encode key + def __key_to_code(self,key): + new_key = getattr(XK, "XK_" + key) + code = self.display.keysym_to_keycode(new_key) + return code + + # reproduce key pressed + def reproduce_key_press(self, key): + Xlib.ext.xtest.fake_input(self.display, Xlib.X.KeyPress, self.__key_to_code(key)) + self.display.sync() + self.keys.append(key) + + # reproduce key release + def reproduce_key_release(self, key): + Xlib.ext.xtest.fake_input(self.display, Xlib.X.KeyRelease, self.__key_to_code(key)) + self.display.sync() + self.keys.remove(key) + + # clean all pressed keys + def clean_up(self): + if self.keys: + while self.keys: + key = self.keys.pop() + self.reproduce_key_release(key) + diff --git a/pcremote-server/services/__init__.py b/pcremote-server/services/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/pcremote-server/services/service.py b/pcremote-server/services/service.py new file mode 100755 index 0000000..778745b --- /dev/null +++ b/pcremote-server/services/service.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Nilson Silva, Jonatas Isvi +# Email : fergus.mao@gmail.com, jonatas.nona@gmail.com +# Reviewer : Jônatas Isvi +# Email : +# Version : 1.0 +# Package : Main Application +# Description : Service Application +# ============================================================================ + +from ObjectServers import * + +class Service: + + """ Service + supports all services applications + """ + + def __init__(self): + self.mouse_srv = None + self.keyboard_srv = None + self.player = None + self.service = "" + self.addr = None + + #Set the Service requested by the Service Manager + def set_service(self, command): + + self.service = command + + if self.service == 'Tablet': + self.mouse_srv = Mouse_Server(self.service) + self.keyboard_srv = KeyBoard_Server(self.service) + elif self.service == 'Slideshow': + self.mouse_srv = Mouse_Server(self.service) + self.keyboard_srv = KeyBoard_Server(self.service) + elif self.service == 'Player': + self.player_srv = Player_Server() + elif self.service == 'Torrent': + print "torrent service." + + #Returns the Service which is being executed + def get_service(self): + return self.service + + #Executes the action requested by the Service Manager + def execute(self, command): + + cmd = command.split(":") + + if cmd[0] == "Mouse": + self.mouse_srv.execute(cmd[1]) + elif cmd[0] == "Keyboard": + self.keyboard_srv.execute(cmd[1]) + elif cmd[0] == "Player": + if self.addr: + cmd += self.addr + self.player_srv.execute(cmd) + else: + self.player_srv.execute(cmd) + + def set_address_to_download(self, addr): + self.addr = addr + + # clean all button and keys pressed + def clean_all(self): + self.mouse_srv.clean_up() + self.keyboard_srv.clean_up() + +#teste unitario +if __name__ == '__main__': + import utils.plistparser diff --git a/pcremote-server/utils/__init__.py b/pcremote-server/utils/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/pcremote-server/utils/labels.py b/pcremote-server/utils/labels.py new file mode 100755 index 0000000..044609c --- /dev/null +++ b/pcremote-server/utils/labels.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +# ============================================================================ +# Project Name : PC Remote +# Author : Jônatas Isvi +# Email : jonatas.nona@gmail.com +# Version : 1.0 +# Description : Labels +# ============================================================================ + +class Labels(): + + def __init__(self): + pass + + # SERVICES SUPPORTED + TABLET = "Tablet" + SLIDESHOW = "Slideshow" + PLAYER = "Player" + TORRENT = "Torrent" + + # GENERIC LABELS FOR MULTIMEDIA APPLICATIONS + + PLAY = "#play" + STOP = "#stop" + PAUSE = "#pause" + NEXT = "#next" + PREVIOUS = "#previous" + VOL_UP = "#vol_up" + VOL_DOWN = "#vol_down" + TLINE_LEFT = "#tline_left" + TLINE_RIGHT = "#tline_right" + RECORD = "#record" + SEEK = "#seek" + LOAD_PLAYLIST = "#load_playlist" + PLAYLIST = "#playlist" + #------------------------------------------> + + # GENERIC LABELS FOR APPLICATIONS + + START = "#start" + CLOSE = "#close" + FULL_SRC = "#fullscreen" + UPLOAD = "#upload" + DOWNLOAD = "#download" + SAVE = "#save" + DELETE = "#delete" + #--------------------------------> + + # GENERAL MOUSE LABELS + + CLICK = "#click" + DOUBLE_CLICK = "#double_click" + TRIPLE_CLICK = "#triple_click" + LEFT_CLICK = "#left_click" + RIGHT_CLICK = "#right_click" + MIDDLE_CLICK = "#middle_click" + #--------------------------------> + + diff --git a/pcremote-server/utils/messages.py b/pcremote-server/utils/messages.py new file mode 100755 index 0000000..b35cd58 --- /dev/null +++ b/pcremote-server/utils/messages.py @@ -0,0 +1,37 @@ +import pynotify +import Image +import StringIO +import gtk + +class Message(): + def __init__(self, AppName): + pynotify.init(AppName) + self.AppName = AppName + self.msgbox = pynotify.Notification(self.AppName, self.AppName, "PCR_on.bmp") + self.msgbox.set_urgency(pynotify.URGENCY_CRITICAL) + self.msgbox.set_timeout(5000) + + def show_message(self, message): + self.msgbox = pynotify.Notification(self.AppName, message) + self.msgbox.show() + + def set_image(self, img): +# image = Image.open(img) +# image = gtk.gdk.pixbuf_new_from_file(img) +# self.msgbox.set_icon_from_pixbuf(self.image2pixbuf(image)) + pass + + def image2pixbuf(self, img): + file1 = StringIO.StringIO() + + img.save(file1, "ppm") + contents = file1.getvalue() + file1.close() + + loader = gtk.gdk.PixbufLoader("pnm") + loader.write(contents, len(contents)) + + pixbuf = loader.get_pixbuf() + loader.close() + + return pixbuf diff --git a/pcremote-server/utils/plistparser.py b/pcremote-server/utils/plistparser.py new file mode 100755 index 0000000..faaa0ac --- /dev/null +++ b/pcremote-server/utils/plistparser.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# **************************************************************************** +# Copyright (c) 2008 INdT/Fucapi. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# ============================================================================ +# Project Name : PC Remote +# Author : Jonatas Isvi +# Email : jonatas.nona@gmail.com +# Reviewer : +# Email : +# Version : 1.0 +# Package : utils +# Description : plisparser +# ============================================================================ + +from xml.etree import cElementTree as ElementTree + +# get the file +def _request(url): + xml = url + return parse_playlist_file(xml) + +# parser the file +def parse_playlist_file(xml): + tree = ElementTree.parse(xml) + listsongs = [] + dictsongs = {} + count = duration = filesize = 0 + title = artist = path = '' + + for child in tree.getiterator(): + if child.tag == 'Title': + title = child.text + elif child.tag == 'Artist': + artist = child.text + elif child.tag == 'Length': + duration = child.text + elif child.tag == 'Filesize': + filesize = child.text + count = count + 1 + dictsongs = {'track' : count, + 'title' : title, + 'artist' : artist, + 'duration' : duration, + 'filesize' : filesize, + 'path' : None, + 'extension' : None, + } + listsongs.append(dictsongs) + + return listsongs + + + + + + + + -- 1.7.9.5