## # @file # This includes the code for the server-side of Wireless-X # # The server running on laptop or PC is responsible for receiving the actions performed by user on the # Wireless-X android app as well as receiving the camera frames of the user's smartphone (if the user has turned) # on the camera). Such actions are transmitted to the server in encoded form, the server decodes the received # message and instructs the laptop or PC to perform the action described in that message. ## import socket import time import threading import autopy import cv2 import numpy as np import binascii import pyfakewebcam import subprocess from pynput.keyboard import Key, Controller as KeyboardController from pynput.mouse import Button, Controller as MouseController ## Creates a virtual camera on the laptop/PC virtualCamera = subprocess.run(["sudo", "modprobe", "v4l2loopback", "devices=1", "video_nr=20", "card_label='Wireless-X Camera'", "exclusive_caps=1"]) print('\n\n----------Wireless-X Server----------\n\n') ## @var width # Stores the width of the screen ## @var height # Stores the height of the screen width, height = autopy.screen.size() ## @var curr_x # Stores the x-coordinate of the current mouse location ## @var curr_y # Stores the y-coordinate of the current mouse location curr_x, curr_y = autopy.mouse.location() ## Stores the mid of x-coordinate of current mouse location remote_x = curr_x/2 ## Stores the mid of y-coordinate of current mouse location remote_y = curr_y/2 ## Socket used for receiving keyboard and mouse related actions s='' ## Socket used for receiving the camera frames of user's smartphone cameraSocket='' ## Width of the camera frame img_width = 720 ## Height of the camera frame img_height = 480 ## Virtual webcam device camera = pyfakewebcam.FakeWebcam('/dev/video20', img_width, img_height) ## The camera and keyboard-mouse sockets receive user requests until this variable is set to 'True' thread_run = True ## Initializing the KeyboardController object keyboard = KeyboardController() ## Initializing the MouseController object mouse = MouseController() ## Speed of mouse movement mouse_speed = 2 ## Screenshot counter to keep track of screenshots screenshot_count = 0 ## # @brief This function establishes two sockets for receiving camera frames as well as mouse and keyboard actions ## def bind_sockets(): """This function creates a camera socket which is responsible for receiving the camera frames, and it also creates another socket which is responsible for receiving the keyboard and mouse frames.""" try: global s, cameraSocket cameraSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) cameraSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) cameraSocket.settimeout(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) cam_port=9998 cameraSocket.bind(("0.0.0.0", cam_port)) #print("Camera stream port: "+ str(cam_port)) cameraSocket.listen(10) key_port=6666 s.bind(("0.0.0.0", key_port)) #print("Keyboard and Mouse Listening port: "+ str(key_port)) s.listen(10) print("\nWireless Server Up and Running ....\n\n") print("Enter Below IP address in your android app") subprocess.run(["hostname", "-I"]) print('\nPress Ctrl+C to terminate the server') except socket.error as msg: print("Socket Binding error: "+str(msg)) time.sleep(5) # Time to sleep before reattempting the bind connection bind_sockets() ## Maps the keys in keyboard layout to the actual keyboard keys special_key_android_dictionary = {"F1": "F1", "F2":"F2", "F3":"F3", "F4":"F4", "F5":"F5", "F6":"F6", "F7":"F7", "F8":"F8", "F9":"F9", "F10":"F10", "F11":"F11", "F12":"F12", "Alt":"ALT", "Backspace":"BACKSPACE", "Caps\nLock":"CAPS_LOCK", "Ctrl":"CONTROL", "Delete":"DELETE", "↓":"DOWN_ARROW", "End":"END", "Esc":"ESCAPE", "Home":"HOME", "←":"LEFT_ARROW", "META":"META", "Page Down":"PAGE_DOWN", "Page Up":"PAGE_UP", "Enter":"RETURN", "→":"RIGHT_ARROW", "Shift":"SHIFT", "Space":"SPACE", "↑":"UP_ARROW", "Tab":"Tab"} ## # @brief This function decodes the received mouse and keyboard actions and acts accordingly ## def mouse_keyboard_connections(): """This function checks if the message received corresponds to a mouse action (left-click, scroll, etc.) or a keyboard action (such as keypress). It then instructs the laptop to perform these actions, using the 'autopy' and 'pynput' libraries. Some special characters in keyboard are not supported by 'autopy' library, so the actions corresponding to these special characters are performed by the 'pynput' library, other key actions are handled by the 'autopy' library. In case of mouse, the 'autopy' library was not that efficient, so we used the 'pynput' library.""" global thread_run, curr_x, curr_y, remote_x, remote_y global screenshot_count thread_run=True prev_x = int(width/2) prev_y = int(height/2) while thread_run: try: conn, address = s.accept() #Mouse coordinates receiving function #print("Mouse Accept") peer_response=str(conn.recv(1024).decode("utf-8")) if "!#Mouse#!" in peer_response: peer_response=peer_response.split("!#Mouse#!")[1] xy=peer_response.split(',') if len(xy)==2: mouse.position = (float(xy[0])*width, float(xy[1])*height) elif "!#Keyboard#!" in peer_response: peer_response=peer_response.split("!#Keyboard#!")[1] if peer_response in special_key_android_dictionary.keys(): print("Special: "+ special_key_android_dictionary[peer_response]) if peer_response == "Backspace": keyboard.press(Key.backspace) keyboard.release(Key.backspace) elif peer_response == "Tab": keyboard.press(Key.tab) keyboard.release(Key.tab) else: autopy.key.tap(eval("autopy.key.Code."+special_key_android_dictionary[peer_response])) elif peer_response == "Cmd": # Windows key or Command Key in Mac keyboard.press(Key.cmd) keyboard.release(Key.cmd) elif peer_response == "Print\nScreen": print("Entered Print Screen: "+ str(screenshot_count)) autopy.bitmap.capture_screen().save(str("screenshot_"+str(screenshot_count)+".png")) screenshot_count+=1 else: print("Not Special :"+peer_response) if peer_response.isalnum and peer_response != "'" and peer_response != "<": print("Is Alpha Numeric") autopy.key.tap(peer_response.lower()) elif peer_response == "'": keyboard.press("'") keyboard.release("'") elif peer_response == "<": keyboard.press("<") keyboard.release("<") elif "!#MouseClick#!" in peer_response: if "LEFT" in peer_response: mouse.click(Button.left, 1) else: mouse.click(Button.right, 1) elif "!#MouseScroll#!" in peer_response: if "SCROLL \nUP" in peer_response: mouse.scroll(0, 2) # Scroll Up else: mouse.scroll(0, -2) # Scroll Down elif "!#Test#!" in peer_response: conn.send(bytes("Success "+str(int(width/mouse_speed))+" "+str(int(height/mouse_speed)),"utf-8")) conn.close() except Exception as msg: pass ## # @brief This function is responsible for handling the camera frames ## def camera_stream_connections(): """This function uses the 'OpenCV' library to decode and resize the camera frame. Then, this frame is scheduled on the virtual webcam device created using 'pyfakewebcam' library.""" thread_run = True image_window_open=False while thread_run: try: conn, address = cameraSocket.accept() client_response = str(conn.recv(1024).decode("utf-8")) while "#$#$#$" not in client_response: client_response += str(conn.recv(1024).decode("utf-8")) pic = client_response.partition("#$#$#$")[0] client_response = client_response.partition("#$#$#$")[2] b = binascii.a2b_base64(pic) nparr = np.frombuffer(b, dtype=np.uint8) frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) img = cv2.resize(frame, (img_width, img_height)) try: camera.schedule_frame(img) except Exception as msg: print ("error: "+ str(msg)) conn.close() except Exception as msg: if "timed out" not in str(msg): print("camera_stream_connections() Error: " + str(msg)) if image_window_open: cv2.destroyWindow("Main") image_window_open=False pass ## # @brief This function is responsible for listening to connections ## def listening_connections(): """This function creates two threads corresponding to the two sockets, one for handling mouse and keyboard events and the other for handling camera frames received from the user's smartphone.""" bind_sockets() mouse_thread=threading.Thread(target=mouse_keyboard_connections) mouse_thread.daemon=True camera_thread=threading.Thread(target=camera_stream_connections) camera_thread.daemon=True mouse_thread.start() camera_thread.start() mouse_thread.join() camera_thread.join() t2=threading.Thread(target=listening_connections) t2.daemon=True t2.start() t2.join()