1#!/usr/local/bin/python3.8 2 3"""Provide an application dock applet for the MATE panel 4 5Create a Mate panel applet and handle events generated 6by it 7 8Note: Functionality for docked apps is provided in docked_app.py 9 10 Function for the dock is provided in dock.py 11 12""" 13 14# Copyright (C) 1997-2003 Free Software Foundation, Inc. 15# 16# This program is free software; you can redistribute it and/or 17# modify it under the terms of the GNU General Public License as 18# published by the Free Software Foundation; either version 2 of the 19# License, or (at your option) any later version. 20# 21# This program is distributed in the hope that it will be useful, but 22# WITHOUT ANY WARRANTY; without even the implied warranty of 23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24# General Public License for more details. 25# 26# You should have received a copy of the GNU General Public License 27# along with this program; if not, write to the Free Software 28# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 29# 02110-1301, USA. 30# 31# Author: 32# Robin Thompson 33 34# do not change the value of this variable - it will be set during build 35# according to the value of the --with-gtk3 option used with .configure 36build_gtk2 = False 37 38import gi 39 40if build_gtk2: 41 gi.require_version("Gtk", "2.0") 42 gi.require_version("Wnck", "1.0") 43else: 44 gi.require_version("Gtk", "3.0") 45 gi.require_version("Wnck", "3.0") 46 47gi.require_version("MatePanelApplet", "4.0") 48 49import os 50import sys 51import threading 52sys.path.insert(1, '@pythondir@') 53 54from Xlib.display import Display 55from Xlib import X, error 56from gi.repository import Gtk 57from gi.repository import MatePanelApplet 58from gi.repository import Gdk 59from gi.repository import Gio 60from gi.repository import GObject 61from gi.repository import GLib 62from gi.repository import Wnck 63 64import xdg.DesktopEntry as DesktopEntry 65from urllib.parse import urlparse 66 67import docked_app 68import dock 69 70from log_it import log_it as log_it 71 72drag_dropped = False # nasty global var used to keep track of whether or not a drag-drop event has occurred 73 74# define a list of keyboard shortcuts to be used to activate specific apps in the dock 75# '<Super>1' to '<Super>0' will correspond to apps 1 to 10 76# '<Super><Alt>1' to '<Super><Alt>9' will correspond to apps 11 to 20 77keyb_shortcuts = ["<Super>1", "<Super>2", "<Super>3", "<Super>4", "<Super>5", 78 "<Super>6", "<Super>7", "<Super>8", "<Super>9", "<Super>0", 79 "<Super><Alt>1", "<Super><Alt>2", "<Super><Alt>3", "<Super><Alt>4", "<Super><Alt>5", 80 "<Super><Alt>6", "<Super><Alt>7", "<Super><Alt>8", "<Super><Alt>9", "<Super><Alt>0"] 81 82 83def applet_button_press(widget, event, the_dock): 84 """Button press event for the applet 85 86 Handle right button press events only 87 88 Find the app that was right clicked and make a record of it 89 90 Args: 91 widget : the widget that was clicked 92 event : the event args 93 the_dock : the Dock object 94 """ 95 96 # we don't get click events for the right mouse button presumably 97 # because the panel hijacks them in order to produce the context menu 98 # However, we do get button press event for the right mouse button, 99 # so we can do what we need to do here .... 100 if event.button == 3: 101 # right click, so save the app that was clicked because 102 # the_dock.app_with_mouse is going to be set to None when the 103 # right click menu appears and we move the mouse over the menu to 104 # select an option 105 app = the_dock.get_app_at_mouse(event.x, event.y) 106 the_dock.right_clicked_app = app 107 108 # because the right click menu is about to be shown, we need to hide 109 # the window list 110 the_dock.hide_win_list() 111 the_dock.hide_act_list() 112 elif event.button == 1: 113 114 if the_dock.panel_expand is not True: 115 # prevent the panel acting on the button press that we got 116 # e.g. so that dragging dock icons does not also start a drag of the panel 117 GObject.signal_stop_emission_by_name(widget, "button_press_event") 118 119 dx, dy = the_dock.get_drag_coords() 120 if (dx == -1) and (dy == -1): 121 the_dock.set_drag_coords(event.x, event.y) 122 123 124def applet_button_release(widget, event, the_dock): 125 """Button press event for the applet 126 127 Handle left button release events only 128 129 If the button is released over a non-running app, start the app 130 131 If the button is released over a running app that isn't on the 132 current workspace, change workspace 133 134 If the button is released over a running app: 135 If the app has only a single window open, activate it 136 If window list is showing, hide it 137 If the window list is not visible, show it 138 139 Since the button has been released, make sure that 140 icon dragging doesn't occur 141 142 Args: 143 widget : the widget that registered the release event 144 event : the event args 145 the_dock : the Dock object 146 147 """ 148 149 if event.button == 1: 150 the_dock.clear_drag_coords() 151 152 # hide popups 153 the_dock.hide_act_list() 154 155 app = the_dock.get_app_at_mouse(event.x, event.y) 156 if app is not None: 157 158 start_app = app.is_running() is False 159 start_app = start_app | (event.state & 160 Gdk.ModifierType.SHIFT_MASK) != 0 161 if start_app: 162 app.start_app() 163 else: 164 if the_dock.win_switch_unity_style: 165 if app.is_active and app.get_num_windows() > 1: 166 the_dock.do_window_selection(app) 167 else: 168 the_dock.minimize_or_restore_windows(app, True) 169 else: 170 # if the app only has a single window minimize or restore it, otherwise 171 # perform the action specified by the user 172 if app.get_num_windows() == 1: 173 the_dock.minimize_or_restore_windows(app) 174 else: 175 the_dock.do_window_selection(app) 176 177 178 # See https://bugs.launchpad.net/ubuntu-mate/+bug/1554128 179 if event.button == 2: 180 app = the_dock.get_app_at_mouse(event.x, event.y) 181 if app is not None: 182 the_dock.hide_win_list() 183 the_dock.hide_act_list() 184 app.start_app() 185 186 187def applet_enter_notify(widget, event, the_dock): 188 """Enter notify event for the applet 189 190 Brighten the icon of the app which the mouse is currently over 191 192 If another app is currently brightened, darken it to normal 193 194 Set up the right click menu for the dock based on the app which 195 the mouse is currently over 196 197 Start the timer for showing app window lists 198 199 Args: 200 widget : the widget that registered the event i.e. the applet 201 event : the event args 202 the_dock : the Dock object 203 """ 204 205 # get the app underneath the mouse cursor 206 app = the_dock.get_app_at_mouse(event.x, event.y) 207 208 # if an app is currently highlighted, de-highlight it 209 if the_dock.app_with_mouse is not None: 210 211 the_dock.app_with_mouse.has_mouse = False 212 the_dock.app_with_mouse.queue_draw() 213 the_dock.app_with_mouse = None 214 215 # highlight the app under the mouse cursor 216 if app is not None: 217 app.has_mouse = True 218 app.queue_draw() 219 220 the_dock.app_with_mouse = app 221 222 # set up the available options for the app 223 the_dock.set_actions_for_app(app) 224 else: 225 the_dock.app_with_mouse = None 226 227 228def applet_leave_notify(widget, event, the_dock): 229 """Leave notify event handle for the applet 230 231 Unbright any brightened app icon 232 233 Args: 234 widget : the widget that registered the event i.e. the applet 235 event : the event args 236 the_dock : the Dock object 237 """ 238 239 if the_dock.app_with_mouse is not None: 240 the_dock.app_with_mouse.has_mouse = False 241 the_dock.app_with_mouse.queue_draw() 242 the_dock.app_with_mouse = None 243 244 the_dock.stop_act_list_timer() 245 if the_dock.scrolling: 246 the_dock.stop_scroll_timer() 247 248 249def applet_motion_notify(widget, event, the_dock): 250 """Motion notify event for the applet 251 252 If the docked app under the mouse cursor does not have its icon 253 brightened and another app has a brightened icon then darken the other app 254 # icon and reset the applet tooltip text 255 256 Then, if the docked app under the mouse cursor does not have its icon 257 brightened then brighten it and setup the applet right click menu 258 259 Args: 260 widget : the widget that registered the event i.e. the applet 261 event : the event args 262 the_dock : the Dock object 263 """ 264 265 app = the_dock.get_app_at_mouse(event.x, event.y) 266 267 if (the_dock.app_with_mouse is not None) and \ 268 (the_dock.app_with_mouse != app): 269 the_dock.app_with_mouse.has_mouse = False 270 the_dock.app_with_mouse.queue_draw() 271 272 widget.queue_draw() 273 274 # because a new app is highlighted reset the window list timer and hide 275 # any currently open window list and action list 276 the_dock.hide_win_list() 277 the_dock.reset_act_list_timer() 278 the_dock.hide_act_list() 279 280 if app is not None: 281 282 the_dock.app_with_mouse = app 283 284 # reset the window list timer 285 the_dock.reset_act_list_timer() 286 287 if the_dock.scrolling and app.scroll_dir != docked_app.ScrollType.SCROLL_NONE: 288 the_dock.reset_scroll_timer() 289 290 if app.has_mouse is False: 291 app.has_mouse = True 292 app.queue_draw() 293 the_dock.app_with_mouse = app 294 the_dock.set_actions_for_app(app) 295 296 else: 297 the_dock.app_with_mouse = None 298 299 the_dock.set_actions_for_app(None) 300 301 dx, dy = the_dock.get_drag_coords() 302 if (dx != -1) and (dy != -1) and not the_dock.dragging: 303 # we may need to begin a drag operation 304 305 if widget.drag_check_threshold(dx, dy, event.x, event.y): 306 target_list = widget.drag_dest_get_target_list() 307 context = widget.drag_begin_with_coordinates(target_list, 308 Gdk.DragAction.MOVE, 1, 309 event, -1, -1) 310 311 applet_drag_begin(widget, context, the_dock) 312 313 314def applet_change_orient(applet, orient, the_dock): 315 """Handler for applet change orientation event 316 317 Set the dock to the new orientation and re-show the applet 318 319 Args: 320 applet : the widget that registered the event i.e. the applet 321 orient : the new orientation 322 the_dock : the Dock object 323 """ 324 325 the_dock.set_new_orientation(orient) 326 the_dock.applet.show_all() 327 the_dock.show_or_hide_app_icons() 328 329 330def applet_size_allocate(applet, allocation, the_dock): 331 """ When the applet can play nicely with panel, ensure that it 332 fits within the allocated space 333 334 335 Args : 336 applet : the applet 337 allocation : a Gtk.Allocation - the space in which the applet must 338 fit 339 the_dock : the Dock object 340 """ 341 342 if the_dock.nice_sizing: 343 the_dock.fit_to_alloc() 344 return 345 346 347def applet_change_size(applet, size, the_dock): 348 """Handler for the applet change size event 349 350 Resize the icon and recalculate the minimize location of each app in the 351 dock 352 353 Args: 354 applet : the widget that registered the event i.e. the applet 355 size : the new applet size 356 the_dock : the Dock object 357 """ 358 359 for app in the_dock.app_list: 360 the_dock.set_app_icon(app, size) 361 362 363def applet_scroll_event(applet, event, the_dock): 364 """ Handler for the scroll event 365 366 Call the dock's function to move forward/backward through the active app's 367 windows 368 369 """ 370 371 # with V0.81 the dock contains a scrolled window and we now only get 372 # a ScrollDirection of SMOOTH here .... 373 if event.direction == Gdk.ScrollDirection.SMOOTH: 374 hasdeltas, dx, dy = event.get_scroll_deltas() 375 if dy < 0: 376 the_dock.do_window_scroll(Gdk.ScrollDirection.DOWN, event.time) 377 elif dy > 0: 378 the_dock.do_window_scroll(Gdk.ScrollDirection.UP, event.time) 379 380 381def applet_drag_begin(applet, context, the_dock): 382 """ 383 Let the dock know we're dragging an icon. 384 Redraw the icon of the app that's being dragged so that the user has 385 visual feedback that the drag has started 386 Set the drag cursor to the app icon 387 Start a timer to monitor the mouse x,y and move the dragged app icon 388 around the dock accordingly 389 390 """ 391 392 # we can sometimes get spurious applet-leave events just before a drag 393 # commences. This causes app_with_mouse to be set to None. Therefore we 394 # may need to identify the app under the mouse ourselves... 395 396 if the_dock.app_with_mouse is None: 397 the_dock.app_with_mouse = the_dock.get_app_under_mouse() 398 399 if the_dock.app_with_mouse is not None: 400 the_dock.app_with_mouse.set_dragee(True) 401 the_dock.app_with_mouse.queue_draw() 402 403 Gtk.drag_set_icon_pixbuf(context, the_dock.app_with_mouse.app_pb, 404 0, 0) 405 406 the_dock.start_drag_motion_timer(the_dock.app_with_mouse) 407 the_dock.dragging = True 408 409 # finally, hide the window list if it was being shown 410 the_dock.hide_win_list() 411 the_dock.hide_act_list() 412 the_dock.stop_scroll_timer() 413 414 415def applet_drag_data_get(widget, drag_context, data, info, time): 416 """ 417 Handler the for drag-data-get event 418 419 Set some dummy text as data for the drag and drop 420 """ 421 422 data.set_text("", -1) 423 424 425def applet_drag_drop(widget, context, x, y, time, the_dock): 426 """ 427 Handler for the drag-drop event 428 429 The drag drop is over so: 430 Call Gtk.drag-finish and indicate the drag and drop completed ok 431 Let the dock know that the drag and drop has finished and redraw 432 the dragged app's icon 433 Stop the timer that monitors the mouse position 434 """ 435 436 app = the_dock.get_dragee() 437 if app is not None: 438 the_dock.stop_drag_motion_timer() 439 app.set_dragee(False) 440 app.queue_draw() 441 Gtk.drag_finish(context, True, False, time) 442 else: 443 # set the drag_dropped module level var so that the drag_data_received event knows 444 # the dnd needs to finish 445 global drag_dropped 446 drag_dropped = True 447 448 target = widget.drag_dest_find_target(context, None) 449 widget.drag_get_data(context, target, time) 450 return True 451 452 453def applet_drag_data_received(widget, drag_context, x, y, data, info, time, the_dock): 454 """ Called when data has been requested from an external source 455 during a drag drop operation 456 457 Examine the data - if it is a .desktop file and the app it relates to 458 is not already in the dock, add it. If the data isn't a .desktop file 459 and the app under the mouse cursor is running, activate it so that 460 the dragged data can be dropped there... 461 462 :param widget: the widget responsible for the event 463 :param drag_context: the dnd context 464 :param x: the x position of the mouse 465 :param y: y the y position of the mouse 466 :param data: the dragged data 467 :param info: 468 :param time: the time of the event 469 :param the_dock: the dock .... 470 """ 471 472 # examine the data -did we get any uris ? 473 uri_list = data.get_uris() 474 if (uri_list is not None) and (len(uri_list) > 0): 475 # when dragging .desktop files to the dock we only allow one to be added at 476 # a time. Therefore we're only interested in the first item in the list 477 uri = urlparse(uri_list[0]) 478 if uri.scheme == "file": 479 # we're looking for a .desktop file 480 if (uri.path != "") and (os.path.split(uri.path)[1].endswith(".desktop")) and \ 481 (os.path.exists(uri.path)): 482 483 # we've got a .desktop file, so if it has been dropped we may need 484 # to add it to the dock 485 global drag_dropped 486 if drag_dropped: 487 # add the .desktop file to the dock if it is not already there,,, 488 the_dock.add_app_to_dock(uri.path) 489 490 # cancel the dnd 491 Gtk.drag_finish(drag_context, True, False, time) 492 drag_dropped = False 493 return 494 else: 495 # the dnd continues .... 496 Gdk.drag_status(drag_context, Gdk.DragAction.COPY, time) 497 return 498 499 # this is not a .desktop so we need to activate the app under the mouse 500 tgt_app = the_dock.get_app_under_mouse() 501 the_dock.start_da_timer(tgt_app) 502 Gdk.drag_status(drag_context, Gdk.DragAction.COPY, time) 503 504 505def applet_drag_end(widget, context, the_dock): 506 """ 507 Handler for the drag-end event 508 509 This will be triggered when e.g. the use drags an icon off the panel and 510 releases the mouse button .... 511 Let the dock know that the drag and drop has finished and redraw the 512 dragged app's icon 513 Stop the timer that monitors the mouse position 514 """ 515 516 the_dock.stop_drag_motion_timer() 517 518 app = the_dock.get_dragee() 519 if app is not None: 520 app.set_dragee(False) 521 app.queue_draw() 522 523 the_dock.dragging = False 524 the_dock.clear_drag_coords() 525 526 527def applet_drag_motion(widget, context, x, y, time, the_dock): 528 """ Handler for the drag-motion event 529 530 :param widget: - the applet 531 :param context: - the dnd context 532 :param x: - x coord of the mouse 533 :param y: - y coord of the mouse 534 :param time: - the time of the event 535 :param the_dock - the dock 536 :return: 537 """ 538 539 # if the applet isn't dragging an app icon, we may need to examine 540 # the dragged data to see what we are dragging 541 app = the_dock.get_dragee() 542 if app is None: 543 # examine the dragged data so we can decide what to do... 544 tgts = context.list_targets() 545 for t in tgts: 546 if t.name() == "text/uri-list": 547 # if the data contains uris, we need to request the data to 548 # see if it contains a .desktop file 549 widget.drag_get_data(context, t, time) 550 return True 551 552 # if the dragged data is anything other than a uri, we just need to activate the app under 553 # the mouse... 554 tgt_app = the_dock.get_app_under_mouse() 555 the_dock.start_da_timer(tgt_app) 556 return True 557 else: 558 # continue the dnd... 559 Gdk.drag_status(context, Gdk.DragAction.COPY, time) 560 561 return True 562 563 564def applet_shortcut_handler(keybinder, the_dock): 565 """ Handler for global keyboard shortcut presses 566 567 Start the app if it isn't already running 568 569 If it is already runnning cycle through its windows ... 570 571 :param keybinder: the keybinder object with the keystring which was pressed e.g. "<Super>4" 572 :param the_dock: the dock... 573 """ 574 # get the position in the dock of the app we need to activate 575 if keybinder.current_shortcut in keybinder.shortcuts: 576 app_no = keybinder.shortcuts.index(keybinder.current_shortcut) 577 578 app = the_dock.get_app_by_pos(app_no) 579 if app is not None: 580 start_app = app.is_running() is False 581 if start_app: 582 app.start_app() 583 else: 584 585 # if the app only has a single window minimize or restore it 586 # otherwise scroll through all available windows 587 if app.get_num_windows() == 1: 588 the_dock.minimize_or_restore_windows(app) 589 else: 590 the_dock.do_window_scroll(Gdk.ScrollDirection.DOWN, 0, app) 591 592 593def applet_fill(applet): 594 """ 595 Create the applet 596 597 Register the events that we're interested in getting events for and 598 connect event handlers for them 599 600 Create a dock and add it V/HBox to the applet 601 602 603 Args: 604 applet : the applet 605 """ 606 607 os.chdir(os.path.expanduser("~")) 608 609 applet.set_events(applet.get_events() | 610 Gdk.EventMask.BUTTON_PRESS_MASK | 611 Gdk.EventMask.BUTTON_RELEASE_MASK | 612 Gdk.EventMask.POINTER_MOTION_MASK | 613 Gdk.EventMask.KEY_PRESS_MASK | 614 Gdk.EventMask.KEY_RELEASE_MASK | 615 Gdk.EventMask.SCROLL_MASK | 616 Gdk.EventMask.STRUCTURE_MASK) 617 618 the_dock = dock.Dock(applet) 619 the_dock.setup_dock() 620 621 if the_dock.nice_sizing: 622 applet.set_flags(MatePanelApplet.AppletFlags.EXPAND_MAJOR | 623 MatePanelApplet.AppletFlags.EXPAND_MINOR | 624 MatePanelApplet.AppletFlags.FLAGS_NONE) 625 else: 626 applet.set_flags(AppletFlags.FLAGS_NONE) 627 628 if build_gtk2: 629 applet.add(the_dock.box) 630 else: 631 applet.add(the_dock.scrolled_win) 632 633 applet.show_all() 634 635 # make sure that apps pinned to specific workspaces other than the current one 636 # are hidden 637 the_dock.show_or_hide_app_icons() 638 639 applet.connect("enter-notify-event", applet_enter_notify, the_dock) 640 applet.connect("leave-notify-event", applet_leave_notify, the_dock) 641 applet.connect("motion-notify-event", applet_motion_notify, the_dock) 642 applet.connect("button-press-event", applet_button_press, the_dock) 643 applet.connect("button-release-event", applet_button_release, the_dock) 644 applet.connect("change-orient", applet_change_orient, the_dock) 645 applet.connect("change-size", applet_change_size, the_dock) 646 applet.connect("scroll-event", applet_scroll_event, the_dock) 647 applet.connect("size-allocate", applet_size_allocate, the_dock) 648 649 if not build_gtk2: 650 # set up drag and drop - gtk3 only 651 # NOTE: we don't get drag-motion events when dragging app icons within the 652 # dock, making it difficult to tell where the mouse pointer is..... 653 # To get around this, dock.py now contains a timer to monitor the 654 # mouse x.y during these sorts of drag and drops. 655 # drag-motion events do fire when dropping from other apps (e.g. caja) 656 # and the drag-motion event is used in these cases 657 658 # we allow .desktop files to be dropped on the applet, so.... 659 drag_tgts = [Gtk.TargetEntry.new("text/uri-list", 0, 0)] 660 applet.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, drag_tgts, 661 Gdk.DragAction.MOVE) 662 663 drag_tgts = [Gtk.TargetEntry.new("text/uri-list", 0, 0)] 664 applet.drag_dest_set(Gtk.DestDefaults.MOTION, None, Gdk.DragAction.COPY) 665 applet.drag_dest_add_image_targets() 666 applet.drag_dest_add_text_targets() 667 applet.drag_dest_add_uri_targets() 668 669 applet.connect("drag-data-get", applet_drag_data_get) 670 applet.connect("drag-drop", applet_drag_drop, the_dock) 671 applet.connect("drag-end", applet_drag_end, the_dock) 672 applet.connect("drag-motion", applet_drag_motion, the_dock) 673 applet.connect("drag-data-received", applet_drag_data_received, the_dock) 674 675 # set up keyboard shortcuts used to activate apps in the dock 676 keybinder = GlobalKeyBinding() 677 for shortcut in keyb_shortcuts: 678 keybinder.grab(shortcut) 679 keybinder.connect("activate", applet_shortcut_handler, the_dock) 680 keybinder.start() 681 682 applet.set_background_widget(applet) # hack for panel transparency 683 684 685def applet_factory(applet, iid, data): 686 """Factory routine called when an applet needs to be created 687 688 Create a dock applet if necessary 689 690 Args: 691 applet : the applet 692 iid : the id of the applet that needs to be created 693 data : 694 Returns: 695 True if we created a dock applet, False otherwise 696 """ 697 698 if iid != "DockApplet": 699 return False 700 701 applet_fill(applet) 702 703 return True 704 705 706class GlobalKeyBinding(GObject.GObject, threading.Thread): 707 __gsignals__ = { 708 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), 709 } 710 711 def __init__(self): 712 GObject.GObject.__init__(self) 713 threading.Thread.__init__(self) 714 self.setDaemon(True) 715 716 self.display = Display() 717 self.screen = self.display.screen() 718 self.window = self.screen.root 719 self.keymap = Gdk.Keymap().get_default() 720 self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) 721 self.map_modifiers() 722 self.shortcuts = [] 723 724 def get_mask_combinations(self, mask): 725 return [x for x in range(mask + 1) if not (x & ~mask)] 726 727 def map_modifiers(self): 728 gdk_modifiers = (Gdk.ModifierType.CONTROL_MASK, Gdk.ModifierType.SHIFT_MASK, Gdk.ModifierType.MOD1_MASK, 729 Gdk.ModifierType.MOD2_MASK, Gdk.ModifierType.MOD3_MASK, Gdk.ModifierType.MOD4_MASK, Gdk.ModifierType.MOD5_MASK, 730 Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK) 731 self.known_modifiers_mask = 0 732 for modifier in gdk_modifiers: 733 if "Mod" not in Gtk.accelerator_name(0, modifier) or "Mod4" in Gtk.accelerator_name(0, modifier): 734 self.known_modifiers_mask |= modifier 735 736 def idle(self): 737 self.emit("activate") 738 return False 739 740 def activate(self): 741 GLib.idle_add(self.run) 742 743 def grab(self, shortcut): 744 keycode = None 745 accelerator = shortcut.replace("<Super>", "<Mod4>") 746 keyval, modifiers = Gtk.accelerator_parse(accelerator) 747 748 try: 749 keycode = self.keymap.get_entries_for_keyval(keyval).keys[0].keycode 750 except AttributeError: 751 # In older Gtk3 the get_entries_for_keyval() returns an unnamed tuple... 752 keycode = self.keymap.get_entries_for_keyval(keyval)[1][0].keycode 753 modifiers = int(modifiers) 754 self.shortcuts.append([keycode, modifiers]) 755 756 # Request to receive key press/release reports from other windows that may not be using modifiers 757 catch = error.CatchError(error.BadWindow) 758 self.window.change_attributes(onerror=catch, event_mask=X.KeyPressMask) 759 if catch.get_error(): 760 return False 761 762 catch = error.CatchError(error.BadAccess) 763 for ignored_mask in self.ignored_masks: 764 mod = modifiers | ignored_mask 765 result = self.window.grab_key(keycode, mod, True, X.GrabModeAsync, X.GrabModeAsync, onerror=catch) 766 self.display.flush() 767 if catch.get_error(): 768 return False 769 return True 770 771 def run(self): 772 self.running = True 773 while self.running: 774 event = self.display.next_event() 775 if (hasattr(event, 'state')): 776 modifiers = event.state & self.known_modifiers_mask 777 self.current_shortcut = None 778 if event.type == X.KeyPress and [event.detail, modifiers] in self.shortcuts: 779 # Track this shortcut to know which app to activate 780 self.current_shortcut = [event.detail, modifiers] 781 GLib.idle_add(self.idle) 782 self.display.allow_events(X.AsyncKeyboard, event.time) 783 else: 784 self.display.allow_events(X.ReplayKeyboard, event.time) 785 786 def stop(self): 787 self.running = False 788 self.ungrab() 789 self.display.close() 790 791 def ungrab(self): 792 for shortcut in self.shortcuts: 793 self.window.ungrab_key(shortcut[0], X.AnyModifier, self.window) 794 795 796MatePanelApplet.Applet.factory_main("DockAppletFactory", True, 797 MatePanelApplet.Applet.__gtype__, 798 applet_factory, None) 799 800 801def main(): 802 """Main function. 803 804 Debugging code can go here 805 """ 806 pass 807 808 809if __name__ == "__main__": 810 main() 811