Esempio n. 1
0
class WM:
    def __init__(self):
        self.display = Display()
        self.root = self.display.screen().root
        self.color_map = self.display.screen().default_colormap
        self.root.change_attributes(event_mask=X.SubstructureRedirectMask
                                    | X.KeyReleaseMask | X.FocusChangeMask)
        self.width = self.root.get_geometry().width
        self.height = self.root.get_geometry().height
        self.windows = []
        self.focused_window = None
        self.actions = [[XK.XK_F, lambda: system("rofi -show")],
                        [XK.XK_X, lambda: self.destroy(self.focused_window)]]
        self.modifier = X.Mod1Mask
        self.next_position = 0
        self.configure()
        system("feh --bg-scale wallpaper.png")

    def getKeyCodes(self, key):
        codes = set(code for code, i in self.display.keysym_to_keycodes(key))
        return list(codes)

    def configure(self):
        for i in self.actions:
            for j in self.getKeyCodes(i[0]):
                self.root.grab_key(j, self.modifier, True, X.GrabModeSync,
                                   X.GrabModeSync)

    def handleMap(self, event):
        event.window.map()
        # event.window.set_input_focus(X.RevertToParent, X.CurrentTime)
        width = self.width // 2 - 40
        height = self.height // 2 - 40
        if self.next_position == 4:
            self.next_position = 0
        if self.next_position == 0:
            x = 0
            y = 0
        elif self.next_position == 1:
            x = self.width // 2
            y = 0
        elif self.next_position == 2:
            x = 0
            y = self.height // 2
        elif self.next_position == 3:
            x = self.width // 2
            y = self.height // 2
        event.window.configure(stack_mode=X.Above,
                               width=width,
                               height=height,
                               x=x,
                               y=y)
        self.next_position += 1
        self.windows.append(event.window)

    def destroy(self, window):
        window.destroy()
        self.windows.remove(window)
        self.next_position -= 1

        width = self.width // 2 - 40
        height = self.height // 2 - 40

        for i, window in enumerate(self.windows):
            if i == 0:
                x = 0
                y = 0
            elif i == 1:
                x = self.width // 2
                y = 0
            elif i == 2:
                x = 0
                y = self.height // 2
            elif i == 3:
                x = self.width // 2
                y = self.height // 2
            window.configure(stack_mode=X.Above,
                             width=width,
                             height=height,
                             x=x,
                             y=y)

    def handleKey(self, event):
        print("Clicked")
        for i in self.actions:
            if event.detail in self.getKeyCodes(i[0]):
                print("Found")
                i[1]()

    def handleEvent(self):
        if self.display.pending_events() > 0:
            event = self.display.next_event()
            print("Got Event:{}".format(event.type))
            if event.type == X.MapRequest:
                self.handleMap(event)
            elif event.type == X.KeyRelease:
                self.handleKey(event)
            elif event.type == X.FocusIn:
                print("Focusing")

    def updateFocus(self):
        window = self.display.screen().root.query_pointer().child
        if window:
            self.focused_window = window
        else:
            self.focused_window = None

    def close(self):
        self.display.close()

    def drawBorder(self, window):

        if window == self.focused_window:
            color = FOCUS_COLOR
        else:
            color = BORDER_COLOR

        color = self.color_map.alloc_named_color(color).pixel
        window.configure(border_width=BORDER_WIDTH)
        window.change_attributes(None, border_pixel=color)
        self.display.sync()

    def loop(self):
        while True:
            self.handleEvent()
            self.updateFocus()
            for i in self.windows:
                self.drawBorder(i)
Esempio n. 2
0
class wm(object):
    """Initialise WM variables and open display"""
    def __init__(self):
        self.windows = []  # List of opened windows
        self.display = Display()  # Initialise display
        self.colormap = self.display.screen(
        ).default_colormap  # Initialise colourmap
        self.rootWindow = self.display.screen().root  # Get root window
        self.activeWindow = None
        self.currentMode = None
        self.activeWindowFullscreen = False
        self.displayWidth = self.rootWindow.get_geometry(
        ).width  # Get width of display
        self.displayHeight = self.rootWindow.get_geometry(
        ).height  # Get height of display
        self.rootWindow.change_attributes(event_mask=X.SubstructureRedirectMask
                                          )  # Redirect events from root window
        self.configureKeys()  # Configure key bindings
        self.readConfig()
        self.workspaces = []
        log("initialized WM and opened display")

    def readConfig(self):
        parser = configparser.ConfigParser()
        parser.read('config.ini')
        self.inactiveColour = parser.get("Theme", "inactive-window-color")
        self.activeColour = parser.get("Theme", "active-window-color")
        self.wallpaper = parser.get("Theme", "wallpaper")
        self.borderSize = parser.get("Theme", "border-size")
        self.startupScriptPath = parser.get("Options", "startup-script-path")
        self.startupScriptCommand = parser.get("Options",
                                               "startup-script-command")
        subprocess.Popen([self.startupScriptCommand, self.startupScriptPath])
        self.defaultBrowser = parser.get("Defaults", "browser")
        self.defaultTerminal = parser.get("Defaults", "terminal")
        self.keymap = parser.get("Options", "keymap")

        subprocess.Popen(['setxkbmap', '-layout', self.keymap])
        if not self.wallpaper == "None":
            subprocess.Popen(["feh", "--bg-scale", self.wallpaper])
        log("read config file")

    def redraw(self):
        self.updateBorders()
        self.handleEvents()
        if len(self.windows) >= 1 and self.activeWindow == None:
            self.activeWindow = self.windows[1]

    def killWindow(self):
        try:
            subprocess.Popen(["killall", self.activeWindowName])
            self.activeWindow.destroy()
            self.windows.remove(self.activeWindow)
            self.activeWindow = None
            log(f"killed window name: {self.activeWindowName}")
        except Exception as e:
            log(f"EXCEPCION on killWindow() : {e} ")

    def updateBorders(self):
        for window in self.windows:
            window.configure(border_width=int(self.borderSize))
            window.change_attributes(None, border_pixel=int(self.borderSize))
            self.display.sync()

    def handleEvents(self):
        ignoredEvents = [3, 33, 34, 23]  # Blacklisted events
        if self.display.pending_events() > 0:
            event = self.display.next_event()  # Get next event from display
        else:
            return
        if event.type == X.MapRequest:
            self.handleMap(event)  # Send mapping events to the mapping handler
        elif event.type == X.KeyPress:
            self.handleKeyPress(
                event)  # Send keypress event to the keypress handler

    """Handle a mapping request"""

    def handleMap(self, event):
        self.windows.append(
            event.window
        )  # Add the window identifier to a list of open windows
        self.activeWindow = event.window  # Set the active window to the mapped window
        self.activeWindowName = event.window.get_wm_name(
        )  # Set the active window name to the window title
        event.window.map()  # Map the window

    def grabKey(self, codes, modifier):
        for code in codes:  # For each code
            self.rootWindow.grab_key(
                code, modifier, 1, X.GrabModeAsync,
                X.GrabModeAsync)  # Receive events when the key is pressed

    def configureKeys(self):
        self.left = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_Left)
        )  # Assign a list of possible keycodes to the variable
        self.right = set(
            code
            for code, index in self.display.keysym_to_keycodes(XK.XK_Right))
        self.up = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_Up))
        self.down = set(
            code
            for code, index in self.display.keysym_to_keycodes(XK.XK_Down))
        self.close = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_X))
        self.enter = set(
            code
            for code, index in self.display.keysym_to_keycodes(XK.XK_Return))
        self.d = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_D))
        self.q = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_Q))
        self.r = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_R))
        self.f = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_F))
        self.esc = set(
            code
            for code, index in self.display.keysym_to_keycodes(XK.XK_Escape))
        self.h = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_H))
        self.j = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_J))
        self.k = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_K))
        self.l = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_L))
        self.g = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_G))
        self.grabbedKeys = [
            self.left, self.g, self.right, self.up, self.down, self.close,
            self.enter, self.d, self.q, self.r, self.f, self.esc, self.h,
            self.j, self.k, self.l
        ]
        for key in self.grabbedKeys:  # For each key to grab,
            self.grabKey(key,
                         X.Mod1Mask)  # Grab the key with the modifer of Alt

    def moveWindow(self, direction):
        log("moved window")
        try:
            if direction == "left":
                windowX = self.activeWindow.get_geometry(
                ).x  # Get the current position of the active window
                self.activeWindow.configure(
                    x=windowX - 5)  # Decrease the X position to move it left
            elif direction == "right":
                windowX = self.activeWindow.get_geometry().x
                self.activeWindow.configure(x=windowX + 5)
            elif direction == "up":
                windowY = self.activeWindow.get_geometry().y
                self.activeWindow.configure(y=windowY - 5)
            elif direction == "down":
                windowY = self.activeWindow.get_geometry().y
                self.activeWindow.configure(y=windowY + 5)
        except AttributeError:
            pass

    def resize(self, direction):
        windowGeometry = self.activeWindow.get_geometry()
        if direction == 0:
            self.activeWindow.configure(width=windowGeometry.width + 5,
                                        x=windowGeometry.x - 5)
        elif direction == 1:
            self.activeWindow.configure(width=windowGeometry.width - 5,
                                        x=windowGeometry.x + 5)
        elif direction == 2:
            self.activeWindow.configure(height=windowGeometry.height - 5,
                                        y=windowGeometry.y + 5)
        else:
            self.activeWindow.configure(height=windowGeometry.height + 5,
                                        y=windowGeometry.y - 5)

    def handleKeyPress(self, event):

        if event.detail in self.left: self.moveWindow("left")
        elif event.detail in self.right: self.moveWindow("right")
        elif event.detail in self.up: self.moveWindow("up")
        elif event.detail in self.down: self.moveWindow("down")
        if event.detail in self.enter:
            self.runProcess(
                self.defaultTerminal)  # Alt+ENTER: Launch a terminal
        elif event.detail in self.d:
            self.runProcess(["rofi", "-show",
                             "run"])  # Alt+D: Launch a program launcher
        elif event.detail in self.q:
            self.killWindow()  # ALT+Q: Close a window
        if event.detail in self.h:
            self.resize(2)
        elif event.detail in self.j:
            self.resize(3)
        elif event.detail in self.k:
            self.resize(1)
        elif event.detail in self.l:
            self.resize(0)

        elif event.detail in self.f:
            self.activeWindow.configure(x=0, width=1920, y=0, height=1080)
        elif event.detail in self.g:
            self.activeWindow.configure(x=0, width=1000, y=0, height=800)

        if event.detail in self.h:
            self.resize(2)
        elif event.detail in self.j:
            self.resize(3)
        elif event.detail in self.k:
            self.resize(1)
        elif event.detail in self.l:
            self.resize(0)

    def runProcess(self, command):
        try:
            subprocess.Popen(command)
        except Exception:
            pass
Esempio n. 3
0
from subprocess import check_output, CalledProcessError
from Xlib.display import Display
from Xlib import X, XK
from Xlib.ext.xtest import fake_input

kobowm_path = str(dirname(realpath(__file__)))
wifi_toggle = ['/home/marek/scripts/wifi/toggle']
usb_toggle = ['/home/marek/scripts/usb/toggle']
sdcard_toggle = ['/home/marek/scripts/sdcard/toggle']
suspend_script = ['/home/marek/scripts/power/suspend']
xterm_launch = ['/usr/bin/xterm', '-e ']
battery_path = '/sys/class/power_supply/mc13892_bat/'

display = Display()
# see kobowm.py
F1 = display.keysym_to_keycodes(XK.XK_F1)[0][0]
F2 = display.keysym_to_keycodes(XK.XK_F2)[0][0]
F3 = display.keysym_to_keycodes(XK.XK_F3)[0][0]
F4 = display.keysym_to_keycodes(XK.XK_F4)[0][0]
F9 = display.keysym_to_keycodes(XK.XK_F9)[0][0]


def battery_status():
    # Returns percentage string and a charging boolean
    val, charging = 0, False
    if exists(battery_path):
        try:
            with open(battery_path + 'capacity', 'r') as f:
                val = int(f.readline().rstrip())
            with open(battery_path + 'status', 'r') as f:
                charging = f.readline().rstrip() == 'Charging'
Esempio n. 4
0
class WM(object):
    def __init__(self):
        self.display = Display()
        self.root_win = self.display.screen().root
        self.go_on = True  # var to keep looping events (or not)
        self.notifs_on = False
        self.poweroff_time = 0
        clear_log()  # clear the log file once
        # kobo touch screen dimension: 600x800px (minus the dock)
        self.full_width = 600
        self.full_height = 740
        self.keyboard_height = 0
        self.keyboard_on = False
        self.active_window = None  # may be transient
        self.top_win_list = []  # list of the top window to cycle in
        self.top_win_pos = -1  # postition in the top win list, -1 means you have to start over
        self.transient_of = {
        }  # key: win, val: set of windows transient for win
        # windows of wm-related apps
        self.wm_keyboard, self.wm_launcher, self.wm_dock, self.wm_notifs = None, None, None, None
        # codes for the keys to catch
        self.f1_codes = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_F1))
        self.f2_codes = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_F2))
        self.f3_codes = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_F3))
        self.f4_codes = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_F4))
        self.f9_codes = set(
            code for code, index in self.display.keysym_to_keycodes(XK.XK_F9))
        XK.load_keysym_group('xf86')
        self.poweroff_codes = set(
            code for code, index in self.display.keysym_to_keycodes(
                XK.XK_XF86_PowerOff))
        self.poweroff_codes.update(
            set(code
                for code, index in self.display.keysym_to_keycodes(XK.XK_F12)))
        self.launch1_codes = set(
            code for code, index in self.display.keysym_to_keycodes(
                XK.XK_XF86_Launch1))
        self.launch1_codes.update(
            set(code
                for code, index in self.display.keysym_to_keycodes(XK.XK_F11)))
        # error catcher
        error_catcher = CatchError(BadAccess)
        self.root_win.change_attributes(
            event_mask=X.SubstructureRedirectMask,
            onerror=error_catcher,
            background_pixel=self.display.screen().white_pixel)
        self.display.sync()
        error = error_catcher.get_error()
        if error:
            sys.exit(1)
        # grab root window key events
        self.grab_root_key(self.poweroff_codes, X.NONE)
        self.grab_root_key(self.launch1_codes, X.NONE)
        self.grab_root_key(self.f1_codes, X.NONE)
        self.grab_root_key(self.f2_codes, X.NONE)
        self.grab_root_key(self.f3_codes, X.NONE)
        self.grab_root_key(self.f4_codes, X.NONE)
        self.grab_root_key(self.f9_codes, X.NONE)
        # handlers
        self.display.set_error_handler(self.x_error_handler)
        self.event_dispatch_table = {
            X.MapRequest: self.handle_map_request,
            X.ConfigureRequest: self.handle_configure_request,
            X.MappingNotify: self.handle_mapping_notify,
            X.UnmapNotify: self.handle_unmapping_notify,
            X.KeyPress: self.handle_key_press,
            X.KeyRelease: self.handle_key_release
        }

    def grab_root_key(self, codes, modifier):
        for code in codes:
            self.root_win.grab_key(code, modifier, 1, X.GrabModeAsync,
                                   X.GrabModeAsync)

    # top level window utility functions

    def win_show(self):
        window = self.top_win_list[self.top_win_pos]  # assumes it exists
        # map the window
        window.map()
        # map transient window if any
        if window in self.transient_of:
            win = None
            for win in self.transient_of[window]:
                win.map()
            # use last transient as active window
            if win:
                self.active_window = win
            else:
                self.active_window = window
        else:
            # set the window as the active one
            self.active_window = window

    def win_hide(self):
        window = self.top_win_list[self.top_win_pos]  # assumes it exists
        # unmap the window
        window.unmap()
        # un map transient window if any
        if window in self.transient_of:
            for win in self.transient_of[window]:
                win.unmap()
        # unset active window
        self.active_window = None

    def win_remove(self):
        window = self.top_win_list.pop(self.top_win_pos)  # assumes it exists
        # remove entry in transient dict if any
        if window in self.transient_of:
            del self.transient_of[window]
        window.destroy()
        self.active_window = None

    # event handling functions

    def x_error_handler(self, err, request):
        log('X protocol error: {0}'.format(err))

    def loop(self):
        # Load every wm app before starting the actual loop
        try:
            self.load_wmapps()
        except KeyboardInterrupt:
            raise
        # Loop until go_on, Ctrl+C or exceptions > MAX_EXCEPTION times.
        errors = 0
        while self.go_on:
            try:
                self.handle_event()
            except KeyboardInterrupt:
                raise
            except:
                errors += 1
                if errors > MAX_EXCEPTIONS:
                    sys.exit(1)
        close_log()
        self.display.close()

    def load_wmapps(self):
        # launch the other apps:
        python_path = find_full_path('python')
        keyboard_path = find_full_path('matchbox-keyboard')
        if not keyboard_path or not python_path:
            raise KeyboardInterrupt
        system([python_path, kobowm_path + '/launcher.py'])
        system([python_path, kobowm_path + '/dock.py'])
        system([python_path, kobowm_path + '/notifzmq.py'])
        system([keyboard_path])
        # catch the events until every wm app has a window
        while not all((self.wm_notifs, self.wm_keyboard, self.wm_launcher,
                       self.wm_dock)):
            try:
                event = self.display.next_event()
            except ConnectionClosedError:
                log('Display connection closed by server')
                raise KeyboardInterrupt
            # ignore events that are not map requests
            if event.type == X.MapRequest:
                event.window.map()
                if str(event.window.get_wm_name()) == 'Keyboard':
                    self.wm_keyboard = event.window
                    self.keyboard_height = event.window.get_geometry().height
                    event.window.configure(x=0,
                                           y=self.full_height -
                                           self.keyboard_height,
                                           width=self.full_width)
                    # catch the keyboard events to later redirect them to the active window
                    event.window.change_attributes(event_mask=X.KeyPressMask
                                                   | X.KeyReleaseMask)
                    event.window.unmap()
                elif str(event.window.get_wm_name()) == 'kobowm-dock':
                    self.wm_dock = event.window
                    event.window.configure(x=0,
                                           y=self.full_height,
                                           width=self.full_width,
                                           height=60)
                    event.window.unmap()
                elif str(event.window.get_wm_name()) == 'kobowm-launcher':
                    self.wm_launcher = event.window
                    event.window.configure(x=0,
                                           y=0,
                                           height=self.full_height,
                                           width=self.full_width)
                    event.window.unmap()
                elif str(event.window.get_wm_name()) == 'kobowm-notifications':
                    self.wm_notifs = event.window
                    event.window.configure(x=380, y=20, height=100, width=200)
                    event.window.unmap()
                else:
                    # there is an unexpexted window: stop
                    raise KeyboardInterrupt
        # finally map again the dock
        self.wm_dock.map()

    def handle_event(self):
        try:
            event = self.display.next_event()
        except ConnectionClosedError:
            log('Display connection closed by server')
            raise KeyboardInterrupt
        if event.type in self.event_dispatch_table:
            self.event_dispatch_table[event.type](event)
        else:
            log('unhandled event: {event}'.format(event=event))

    def handle_configure_request(self, event):
        window = event.window
        args = {'border_width': 1}
        if event.value_mask & X.CWX:
            args['x'] = event.x
        if event.value_mask & X.CWY:
            args['y'] = event.y
        if event.value_mask & X.CWWidth:
            args['width'] = event.width
        if event.value_mask & X.CWHeight:
            args['height'] = event.height
        if event.value_mask & X.CWSibling:
            args['sibling'] = event.above
        if event.value_mask & X.CWStackMode:
            args['stack_mode'] = event.stack_mode
        window.configure(**args)

    def handle_map_request(self, event):
        event.window.map()
        # handle transient windows
        transient_for = event.window.get_wm_transient_for()
        if transient_for:
            self.active_window = event.window
            if transient_for not in self.transient_of:
                self.transient_of[transient_for] = []
            self.transient_of[transient_for].append(event.window)
        else:
            if self.active_window:
                self.active_window.unmap()
            self.active_window = event.window
            self.top_win_list.append(event.window)
            self.top_win_pos = len(self.top_win_list) - 1
        # use all the available screen
        self.active_window.configure(x=-1,
                                     y=-1,
                                     width=self.full_width,
                                     height=self.full_height)
        # hide the keyboard if it was open
        if self.keyboard_on:
            self.wm_keyboard.unmap()
            self.keyboard_on = False

    def handle_mapping_notify(self, event):
        # necessary by documentation
        self.display.refresh_keyboard_mapping(event)

    def handle_unmapping_notify(self, event):
        # used to handle windows that get unmapped not by this wm directly
        transient_for = event.window.get_wm_transient_for()
        if transient_for:
            self.transient_of[transient_for].remove(event.window)
            if self.active_window == event.window:
                self.active_window = transient_for
        elif event.window in self.top_win_list:
            self.top_win_list.remove(event.window)
            if self.active_window == event.window:
                self.active_window = None
                self.top_win_pos = -1

    def handle_key_press(self, event):
        if event.window == self.wm_keyboard:
            return  # do nothing: will be sent to the active window on release
        if event.detail in self.poweroff_codes:
            self.go_on = False
        elif event.detail in self.launch1_codes:
            self.action_apps()
        elif event.detail in self.f1_codes:
            self.action_tasks()
        elif event.detail in self.f2_codes:
            self.action_keyboard()
        elif event.detail in self.f3_codes:
            system(XTERM_COMMAND)
        elif event.detail in self.f4_codes:
            self.action_close()
        elif event.detail in self.f9_codes:
            self.action_notifs()

    def handle_key_release(self, event):
        if event.window == self.wm_keyboard:
            # focus on the active window and emulate key press/release
            self.display.sync()
            self.display.set_input_focus(self.active_window, X.RevertToParent,
                                         X.CurrentTime)
            fake_input(self.display, X.KeyPress, event.detail)
            fake_input(self.display, X.KeyRelease, event.detail)
            self.display.sync()

    # action performer functions

    def action_apps(self):
        if not self.active_window:
            self.active_window = self.wm_launcher
            self.active_window.map()
        elif self.active_window == self.wm_launcher:
            self.active_window.unmap()
            self.active_window = None
        else:
            # hide the active window (and the stack if it's transient)
            self.win_hide()
            # start over with the cycling
            self.top_win_pos = -1
            self.active_window = self.wm_launcher
            self.active_window.map()

    def action_tasks(self):
        if not self.top_win_list:
            return
        if self.top_win_pos != -1 and self.top_win_pos < len(
                self.top_win_list):
            self.win_hide()
        self.top_win_pos += 1
        if self.top_win_pos >= len(self.top_win_list):
            self.top_win_pos = 0
        self.win_show()

    def action_keyboard(self):
        if self.keyboard_on:
            self.wm_keyboard.unmap()
            self.active_window.configure(height=self.full_height)
            self.keyboard_on = False
        elif self.active_window and self.active_window != self.wm_launcher:
            self.wm_keyboard.map()
            self.active_window.configure(height=self.full_height -
                                         self.keyboard_height)
            self.wm_keyboard.configure(stack_mode=X.Above)
            self.keyboard_on = True

    def action_close(self):
        if not self.active_window:
            return  # nothing to close here
        if self.active_window in self.top_win_list:
            self.win_remove()
        else:
            # it's a transient window
            transient_for = self.active_window.get_wm_transient_for()
            self.transient_of[transient_for].remove(self.active_window)
            self.active_window.destroy()
            self.active_window = transient_for
        # close the keyboard if open
        if self.keyboard_on:
            self.wm_keyboard.unmap()
            self.keyboard_on = False

    def action_notifs(self):
        if self.notifs_on:
            self.wm_notifs.unmap()
            log('Notification window hidden')
        else:
            self.wm_notifs.configure(stack_mode=X.Above)
            self.wm_notifs.map()
            log('Notification window visible')
        self.notifs_on = not self.notifs_on
Esempio n. 5
0
class Keyboard(KeyboardMeta):
    """
    The PyKeyboard implementation for X11 systems (mostly linux). This
    allows one to simulate keyboard input.
    """
    def __init__(self, display=None):
        PyKeyboardMeta.__init__(self)
        self.display = Display(display)
        self.root = self.display.screen().root
        
        XK.load_keysym_group('xkb')
        
        altList = self.display.keysym_to_keycodes(XK.XK_ISO_Level3_Shift)
        self.__usable_modifiers = (0, 1)
        for code, offset in altList:
            if code == 108 and offset == 0:
                self.__usable_modifiers += (4, 5)
                break
            
        mapping = self.display.get_modifier_mapping()
        self.modmasks = {}
        for keyname in MODIFIERS:
            keysym = XK.string_to_keysym(keyname)
            keycodes = self.display.keysym_to_keycodes(keysym)
            
            found = False
            for keycode, lvl in keycodes:
                for index, mask in MASK_INDEXES:
                    if keycode in mapping[index]:
                        self.modmasks[keycode] = mask
                        found = True
                    
                if found:
                    break
 
        self.flags = {        
            'Shift': X.ShiftMask,
            'Lock': X.LockMask,
            'Ctrl': X.ControlMask,
            'Alt': 0,
            'AltGr': self.modmasks[altList[0][0]],
            'Hankaku': 0}
        
        self.special_key_assignment()

    def __findUsableKeycode(self, keycodes):
        for code, mask in keycodes:
            if mask in self.__usable_modifiers:
                return code, mask

        return None, None

    def press_key(self, character='', modifier=0):
        """
        Press a given character key. Also works with character keycodes as
        integers, but not keysyms.
        """
        window = self.display.get_input_focus().focus
        
        char_val, char_mask = self.lookup_character_value(character)
        if char_val == None or char_mask == None:
            return False
        char_mask ^= modifier
        
        print character, char_mask, modifier
        
        event = protocol.event.KeyPress(
            detail = char_val,
            time = X.CurrentTime,
            root = self.root,
            window = window,
            child = X.NONE,
            root_x = 0,
            root_y = 0,
            event_x = 0,
            event_y = 0,
            state = char_mask,
            same_screen = 0)
        window.send_event(event)
        self.display.sync()

    def release_key(self, character='', modifier=0):
        """
        Release a given character key. Also works with character keycodes as
        integers, but not keysyms.
        """
        window = self.display.get_input_focus().focus
        
        char_val, char_mask = self.lookup_character_value(character)
        if char_val == None or char_mask == None:
            return False
        char_mask ^= modifier
        
        event = protocol.event.KeyRelease(
            detail = char_val,
            time = X.CurrentTime,
            root = self.root,
            window = window,
            child = X.NONE,
            root_x = 0,
            root_y = 0,
            event_x = 0,
            event_y = 0,
            state = char_mask,
            same_screen = 0)
        window.send_event(event)
        self.display.sync()

    def special_key_assignment(self):
        """
        Determines the keycodes for common special keys on the keyboard. These
        are integer values and can be passed to the other key methods.
        Generally speaking, these are non-printable codes.
        """
        #This set of keys compiled using the X11 keysymdef.h file as reference
        #They comprise a relatively universal set of keys, though there may be
        #exceptions which may come up for other OSes and vendors. Countless
        #special cases exist which are not handled here, but may be extended.
        #TTY Function Keys
        self.backspace_key = self.lookup_character_value('BackSpace')[0]
        self.tab_key = self.lookup_character_value('Tab')[0]
        self.linefeed_key = self.lookup_character_value('Linefeed')[0]
        self.clear_key = self.lookup_character_value('Clear')[0]
        self.return_key = self.lookup_character_value('Return')[0]
        self.enter_key = self.return_key  # Because many keyboards call it "Enter"
        self.pause_key = self.lookup_character_value('Pause')[0]
        self.scroll_lock_key = self.lookup_character_value('Scroll_Lock')[0]
        self.sys_req_key = self.lookup_character_value('Sys_Req')[0]
        self.escape_key = self.lookup_character_value('Escape')[0]
        self.delete_key = self.lookup_character_value('Delete')[0]
        #Modifier Keys
        self.shift_l_key = self.lookup_character_value('Shift_L')[0]
        self.shift_r_key = self.lookup_character_value('Shift_R')[0]
        self.shift_key = self.shift_l_key  # Default Shift is left Shift
        self.alt_l_key = self.lookup_character_value('Alt_L')[0]
        self.alt_r_key = self.lookup_character_value('Alt_R')[0]
        self.alt_key = self.alt_l_key  # Default Alt is left Alt
        self.alt_gr_key = self.lookup_character_value('ISO_Level3_Shift')[0]
        self.control_l_key = self.lookup_character_value('Control_L')[0]
        self.control_r_key = self.lookup_character_value('Control_R')[0]
        self.control_key = self.control_l_key  # Default Ctrl is left Ctrl
        self.caps_lock_key = self.lookup_character_value('Caps_Lock')[0]
        self.capital_key = self.caps_lock_key  # Some may know it as Capital
        self.shift_lock_key = self.lookup_character_value('Shift_Lock')[0]
        self.meta_l_key = self.lookup_character_value('Meta_L')[0]
        self.meta_r_key = self.lookup_character_value('Meta_R')[0]
        self.super_l_key = self.lookup_character_value('Super_L')[0]
        self.windows_l_key = self.super_l_key  # Cross-support; also it's printed there
        self.super_r_key = self.lookup_character_value('Super_R')[0]
        self.windows_r_key = self.super_r_key  # Cross-support; also it's printed there
        self.hyper_l_key = self.lookup_character_value('Hyper_L')[0]
        self.hyper_r_key = self.lookup_character_value('Hyper_R')[0]
        #Cursor Control and Motion
        self.home_key = self.lookup_character_value('Home')[0]
        self.up_key = self.lookup_character_value('Up')[0]
        self.down_key = self.lookup_character_value('Down')[0]
        self.left_key = self.lookup_character_value('Left')[0]
        self.right_key = self.lookup_character_value('Right')[0]
        self.end_key = self.lookup_character_value('End')[0]
        self.begin_key = self.lookup_character_value('Begin')[0]
        self.page_up_key = self.lookup_character_value('Page_Up')[0]
        self.page_down_key = self.lookup_character_value('Page_Down')[0]
        self.prior_key = self.lookup_character_value('Prior')[0]
        self.next_key = self.lookup_character_value('Next')[0]
        #Misc Functions
        self.select_key = self.lookup_character_value('Select')[0]
        self.print_key = self.lookup_character_value('Print')[0]
        self.print_screen_key = self.print_key  # Seems to be the same thing
        self.snapshot_key = self.print_key  # Another name for printscreen
        self.execute_key = self.lookup_character_value('Execute')[0]
        self.insert_key = self.lookup_character_value('Insert')[0]
        self.undo_key = self.lookup_character_value('Undo')[0]
        self.redo_key = self.lookup_character_value('Redo')[0]
        self.menu_key = self.lookup_character_value('Menu')[0]
        self.apps_key = self.menu_key  # Windows...
        self.find_key = self.lookup_character_value('Find')[0]
        self.cancel_key = self.lookup_character_value('Cancel')[0]
        self.help_key = self.lookup_character_value('Help')[0]
        self.break_key = self.lookup_character_value('Break')[0]
        self.mode_switch_key = self.lookup_character_value('Mode_switch')[0]
        self.script_switch_key = self.lookup_character_value('script_switch')[0]
        self.num_lock_key = self.lookup_character_value('Num_Lock')[0]
        #Keypad Keys: Dictionary structure
        keypad = ['Space', 'Tab', 'Enter', 'F1', 'F2', 'F3', 'F4', 'Home',
                  'Left', 'Up', 'Right', 'Down', 'Prior', 'Page_Up', 'Next',
                  'Page_Down', 'End', 'Begin', 'Insert', 'Delete', 'Equal',
                  'Multiply', 'Add', 'Separator', 'Subtract', 'Decimal',
                  'Divide', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        self.keypad_keys = {k: self.lookup_character_value('KP_'+str(k)[0]) for k in keypad}
        self.numpad_keys = self.keypad_keys
        #Function Keys/ Auxilliary Keys
        #FKeys
        self.function_keys = [None] + [self.lookup_character_value('F'+str(i)[0]) for i in xrange(1,36)]
        #LKeys
        self.l_keys = [None] + [self.lookup_character_value('L'+str(i)[0]) for i in xrange(1,11)]
        #RKeys
        self.r_keys = [None] + [self.lookup_character_value('R'+str(i)[0]) for i in xrange(1,16)]

        #Unsupported keys from windows
        self.kana_key = None
        self.hangeul_key = None # old name - should be here for compatibility
        self.hangul_key = None
        self.junjua_key = None
        self.final_key = None
        self.hanja_key = None
        self.kanji_key = None
        self.convert_key = None
        self.nonconvert_key = None
        self.accept_key = None
        self.modechange_key = None
        self.sleep_key = None

    def lookup_character_value(self, character):
        """
        Looks up the keysym for the character then returns the keycode mapping
        and modifier for that keysym.
        """
        ch_keysym = XK.string_to_keysym(character)
        ch_mask = 0
        if ch_keysym == 0:
            if character in SPECIAL_X_KEYSYMS:
                ch_keysym = XK.string_to_keysym(SPECIAL_X_KEYSYMS[character])
            elif len(character) == 1:
                ch_keysym = ord(character)
        ch_keycodes = self.display.keysym_to_keycodes(ch_keysym)
        
        if len(ch_keycodes) == 0 and len(character) == 1:
            ch_keycodes = self.display.keysym_to_keycodes(ord(character.lower()))
            ch_mask ^= X.LockMask
            
        if len(ch_keycodes) > 0:
            ch_keycode, mask = self.__findUsableKeycode(ch_keycodes)
            if ch_keycode == None or mask == None:
                return None, None
            else:
                ch_mask ^= mask
        else:
            return None, None
        
        if ch_mask ^ 4 < 4:
            ch_mask ^= 4
            ch_mask ^= self.modmasks[self.alt_gr_key]
        
        return ch_keycode, ch_mask
Esempio n. 6
0
class wm(object):
    """Initialise WM variables and open display"""
    def __init__(self):
        self.windowList = []  # List of opened windows
        self.display = Display()  # Initialise display
        self.colormap = self.display.screen().default_colormap  # Initialise colourmap
        self.currentMode = None
        self.activeWindow = None
        self.rootWindow = self.display.screen().root  # Get root window
        self.displayWidth = self.rootWindow.get_geometry().width  # Get width of display
        self.displayHeight = self.rootWindow.get_geometry().height  # Get height of display
        self.rootWindow.change_attributes(event_mask = X.SubstructureRedirectMask)  # Redirect events from root window
        self.configureKeys()  # Configure key bindings

    def mainLoop(self):
        self.updateFocus()
        self.updateBorders()
        self.handleEvents()
        log(3, "Mode:"+str(self.currentMode))

    """Destroy an active window"""
    def destroyWindow(self, event):
        try:
            self.activeWindow.destroy()
            self.windowList.remove(self.activeWindow)
            self.activeWindow = None
        except:
            log(2, "No focused window!")
 
    def updateFocus(self):
        window = self.display.screen().root.query_pointer().child
        if window != 0:
            self.activeWindow = window

    def updateBorders(self):
        for window in self.windowList:
            #gc = self.rootWindow.create_gc()
            #window.fill_rectangle(gc, 0, window.get_geometry().y-5, window.get_geometry().width, 5)
            #window.draw_text(gc, window.get_geometry().width/2, 5, "Hello, World!", "ff0011")
            if window != self.activeWindow:
                borderColour = preferences.theme.border.inactiveColour
            else:
                borderColour = preferences.theme.border.activeColour
            borderColour = self.colormap.alloc_named_color(borderColour).pixel #borderColour).pixel
            window.configure(border_width = preferences.theme.border.borderWidth)
            window.change_attributes(None,border_pixel=borderColour)
            self.display.sync()

    """Handle WM events"""
    def handleEvents(self):
        ignoredEvents = [3, 33, 34, 23]  # Blacklisted events
        if self.display.pending_events() > 0:
            event = self.display.next_event()  # Get next event from display 
        else:
            return
        if event.type == X.MapRequest: self.handleMap(event)  # Send mapping events to the mapping handler
        elif event.type == X.KeyPress: self.handleKeyPress(event)  # Send keypress event to the keypress handler
        elif event.type in ignoredEvents: log(3, "Ignoring event: "+str(event.type))  # Ignore event if it is a blacklisted event
        else:  # Otherwise, if the event is not a currently handled event
            log(1, "Unhandled event: "+str(event.type))  # Warn of an unhandled event

    """Handle a mapping request"""
    def handleMap(self, event):
        self.windowList.append(event.window)  # Add the window identifier to a list of open windows
        log(3, str(self.windowList))  # Show list of windows (DEBUG)
        self.activeWindow = event.window  # Set the active window to the mapped window
        self.activeWindowName = event.window.get_wm_name()  # Set the active window name to the window title
        event.window.map()  # Map the window

    """Receive a KeyPressed event when a certain key is pressed"""
    def grabKey(self, codes, modifier):
        for code in codes:  # For each code
            self.rootWindow.grab_key(code, modifier, 1, X.GrabModeAsync, X.GrabModeAsync)  # Receive events when the key is pressed

    """Bind keys"""
    def configureKeys(self):
        self.left = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_Left))  # Assign a list of possible keycodes to the variable
        self.right = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_Right))
        self.up = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_Up))
        self.down = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_Down))
        self.close = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_X))
        self.t = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_T))
        self.e = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_E))
        self.x = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_X))
        self.r = set(code for code, index in self.display.keysym_to_keycodes(XK.XK_X))
        self.grabbedKeys = [self.left, self.right, self.up, self.down, self.close, self.t, self.e, self.x, self.r]
        for key in self.grabbedKeys:  # For each key to grab,
            self.grabKey(key, X.Mod1Mask)  # Grab the key with the modifer of Alt

    #def resizeWindow(window, direction):
    #    windowW = window.get_geometry().width
    #    windowH = window.get_geometry().height
    #    if direction == 0: window.configure(width=windowW-1)
    #    elif direction == 1: window.configure(width=windowW+1)
    #    elif direction == 2: window.configure(height=windowH-1)
    #    elif direction == 3: window.configure(height=windowH+1)


    """Change the position of an active window"""
    def moveWindow(self, direction):
        try:
            if direction == "left":
                windowX = self.activeWindow.get_geometry().x  # Get the current position of the active window
                self.activeWindow.configure(x=windowX-5)  # Decrease the X position to move it left
            elif direction == "right":
                windowX = self.activeWindow.get_geometry().x
                self.activeWindow.configure(x=windowX+5)
            elif direction == "up":
                windowY = self.activeWindow.get_geometry().y
                self.activeWindow.configure(y=windowY-5)    
            elif direction == "down":
                windowY = self.activeWindow.get_geometry().y
                self.activeWindow.configure(y=windowY+5)
            else:
                log(1, "Invalid movement direction!")  
        except AttributeError:
                log(1, "No focused window!")

    """Handle key presses"""
    def handleKeyPress(self, event): 
      #  if self.currentMode == "resize":                                                             # If resize mode is enabled:
            #if event.detail in self.left: self.resizeWindow(event.window, 0)                        # Alt+Left Arrow: resize window (x-1)
           # elif event.detail in self.right: self.moveWindow(event.window, 1)                       # Alt+Right Arrow: resize window (x+1)
          #  elif event.detail in self.up: self.moveWindow(event.window, 2)                          # Alt+Up Arrow: resize window (y-1)
         #   elif event.detail in self.down: self.moveWindow(event.window, 3)                        # Alt+Down Arrow: resize window (y+1)
     #   else:                                                                                       # Otherwise,
        if event.detail in self.left: self.moveWindow("left")                                   # Alt+Left Arrow: move a window left
        elif event.detail in self.right: self.moveWindow("right")                               # Alt+Right Arrow: move a window right
        elif event.detail in self.up: self.moveWindow("up")                                     # Alt+Up Arrow: move a window up
        elif event.detail in self.down: self.moveWindow("down")                                 # Alt+Down Arrow: move a window down
        elif event.detail in self.t: self.runProcess(preferences.applicationDefaults.terminal)      # Alt+T: Launch a terminal
        elif event.detail in self.e: self.runProcess(preferences.applicationDefaults.launcher)      # Alt+E: Launch a program launcher
        elif event.detail in self.x: self.destroyWindow(event)                                      # ALT+X: Close a window
        elif event.detail in self.r: self.currentMode = "resize"                                    # ALT+R: Enable resize mode
        else:
            log(1, "Unhandled key event!")

    """Close connection to display server"""
    def closeDisplay(self):
        log(0, "Exiting...")
        self.display.close()

    """Run an application/process"""
    def runProcess(self, applicationInfo):
        try:
            name = applicationInfo["name"]  # Get application name
            command = applicationInfo["command"]  # Get aplication command 
        except:
            raise ValueError
        try:
            log(0, "Running: "+name)
            subprocess.Popen(command)  # Run the command and disown the child process
        except BaseException as error:
            log(2, "Failed to launch process: "+processCommand+"!")
            log(3, str(error))