| 1 | # Copyright (c) 2008-2009, samurai-x.org |
|---|
| 2 | # All rights reserved. |
|---|
| 3 | # |
|---|
| 4 | # Redistribution and use in source and binary forms, with or without |
|---|
| 5 | # modification, are permitted provided that the following conditions are met: |
|---|
| 6 | # * Redistributions of source code must retain the above copyright |
|---|
| 7 | # notice, this list of conditions and the following disclaimer. |
|---|
| 8 | # * Redistributions in binary form must reproduce the above copyright |
|---|
| 9 | # notice, this list of conditions and the following disclaimer in the |
|---|
| 10 | # documentation and/or other materials provided with the distribution. |
|---|
| 11 | # * Neither the name of the samurai-x.org nor the |
|---|
| 12 | # names of its contributors may be used to endorse or promote products |
|---|
| 13 | # derived from this software without specific prior written permission. |
|---|
| 14 | # |
|---|
| 15 | # THIS SOFTWARE IS PROVIDED BY SAMURAI-X.ORG ``AS IS'' AND ANY |
|---|
| 16 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|---|
| 17 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|---|
| 18 | # DISCLAIMED. IN NO EVENT SHALL SAMURAI-X.ORG BE LIABLE FOR ANY |
|---|
| 19 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|---|
| 20 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|---|
| 21 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|---|
| 22 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| 23 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|---|
| 24 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 25 | |
|---|
| 26 | """ |
|---|
| 27 | sx-clientbuttons is a plugin that enables you to bind mouse button |
|---|
| 28 | and modifier key combinations to an action. |
|---|
| 29 | |
|---|
| 30 | Configuration |
|---|
| 31 | ------------- |
|---|
| 32 | |
|---|
| 33 | .. attribute:: clientbuttons.bindings |
|---|
| 34 | |
|---|
| 35 | Dictionary of button/modifier (:ref:`buttonstrokes`): action. |
|---|
| 36 | For example:: |
|---|
| 37 | |
|---|
| 38 | 'clientbuttons.bindings': { |
|---|
| 39 | 'Meta+1': 'moveresize.move', |
|---|
| 40 | 'Meta+3': 'moveresize.resize', |
|---|
| 41 | } |
|---|
| 42 | |
|---|
| 43 | """ |
|---|
| 44 | |
|---|
| 45 | import logging |
|---|
| 46 | log = logging.getLogger(__name__) |
|---|
| 47 | |
|---|
| 48 | from samuraix import config |
|---|
| 49 | from samuraix.plugin import Plugin |
|---|
| 50 | from samuraix.util import parse_buttonstroke |
|---|
| 51 | |
|---|
| 52 | from ooxcb.protocol import xproto |
|---|
| 53 | from ooxcb.protocol.xproto import EventMask |
|---|
| 54 | |
|---|
| 55 | from sxactions import ActionInfo |
|---|
| 56 | |
|---|
| 57 | |
|---|
| 58 | class SXClientButtons(Plugin): |
|---|
| 59 | """ Plugin to allow binding mouse actions to actions """ |
|---|
| 60 | |
|---|
| 61 | key = 'clientbuttons' |
|---|
| 62 | |
|---|
| 63 | def __init__(self, app): |
|---|
| 64 | self.app = app |
|---|
| 65 | app.push_handlers(self) |
|---|
| 66 | self.bindings = {} |
|---|
| 67 | |
|---|
| 68 | def on_load_config(self, config): |
|---|
| 69 | for stroke, action in config.get('clientbuttons.bindings', {}).iteritems(): |
|---|
| 70 | buttonstroke = parse_buttonstroke(stroke) |
|---|
| 71 | self.bindings[buttonstroke] = action |
|---|
| 72 | |
|---|
| 73 | def on_ready(self, app): |
|---|
| 74 | for screen in app.screens: |
|---|
| 75 | screen.push_handlers(self) |
|---|
| 76 | |
|---|
| 77 | for client in screen.clients: |
|---|
| 78 | self.on_new_client(screen, client) |
|---|
| 79 | |
|---|
| 80 | def on_new_client(self, screen, client): |
|---|
| 81 | def on_button_press(evt): |
|---|
| 82 | stroke = (evt.state, evt.detail) |
|---|
| 83 | if stroke in self.bindings: |
|---|
| 84 | info = ActionInfo(screen=self.app.get_screen_by_root(evt.root), |
|---|
| 85 | x=evt.event_x, y=evt.event_y, |
|---|
| 86 | client=client) |
|---|
| 87 | self.app.plugins['actions'].emit(self.bindings[stroke], info) |
|---|
| 88 | |
|---|
| 89 | # grab the specific button and modifier to allow the app to keep working! |
|---|
| 90 | for modifier, button in self.bindings.iterkeys(): |
|---|
| 91 | client.window.grab_button(EventMask.ButtonPress, |
|---|
| 92 | button, |
|---|
| 93 | modifier, |
|---|
| 94 | False, |
|---|
| 95 | xproto.GrabMode.Sync, |
|---|
| 96 | xproto.GrabMode.Sync |
|---|
| 97 | ) |
|---|
| 98 | |
|---|
| 99 | client.window.push_handlers(on_button_press=on_button_press) |
|---|
| 100 | |
|---|
| 101 | def on_unmanage_client(self, screen, client): |
|---|
| 102 | # tidy up properly, but only if the actor exists |
|---|
| 103 | if client.actor.valid: |
|---|
| 104 | for modifier, button in self.bindings.iterkeys(): |
|---|
| 105 | client.window.ungrab_button(button, modifier) |
|---|
| 106 | |
|---|
| 107 | |
|---|