~ubuntu-branches/ubuntu/karmic/parti-all/karmic

« back to all changes in this revision

Viewing changes to wimpiggy/selection.py

  • Committer: Bazaar Package Importer
  • Author(s): Evan Dandrea
  • Date: 2009-06-02 12:44:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090602124400-xbkgh9vxjciey732
Tags: upstream-0.0.6
ImportĀ upstreamĀ versionĀ 0.0.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# This file is part of Parti.
 
2
# Copyright (C) 2008, 2009 Nathaniel Smith <njs@pobox.com>
 
3
# Parti is released under the terms of the GNU GPL v2, or, at your option, any
 
4
# later version. See the file COPYING for details.
 
5
 
 
6
# According to ICCCM 2.8/4.3, a window manager for screen N is a client which
 
7
# acquires the selection WM_S<N>.  If another client already has this
 
8
# selection, we can either abort or steal it.  Once we have it, if someone
 
9
# else steals it, then we should exit.
 
10
 
 
11
import gobject
 
12
import gtk
 
13
from struct import pack, unpack
 
14
import time
 
15
 
 
16
from wimpiggy.util import no_arg_signal, one_arg_signal
 
17
from wimpiggy.lowlevel import (get_xatom, get_pywindow, sendClientMessage,
 
18
                               myGetSelectionOwner, const,
 
19
                               add_event_receiver, remove_event_receiver)
 
20
from wimpiggy.error import *
 
21
 
 
22
from wimpiggy.log import Logger
 
23
log = Logger()
 
24
 
 
25
class AlreadyOwned(Exception):
 
26
    pass
 
27
 
 
28
class ManagerSelection(gobject.GObject):
 
29
    __gsignals__ = {
 
30
        "selection-lost": no_arg_signal,
 
31
 
 
32
        "wimpiggy-destroy-event": one_arg_signal,
 
33
        }
 
34
 
 
35
    def __init__(self, display, selection):
 
36
        gobject.GObject.__init__(self)
 
37
        self.atom = selection
 
38
        self.clipboard = gtk.Clipboard(display, selection)
 
39
 
 
40
    def _owner(self):
 
41
        return myGetSelectionOwner(self.clipboard, self.atom)
 
42
 
 
43
    def owned(self):
 
44
        "Returns True if someone owns the given selection."
 
45
        return self._owner() != const["XNone"]
 
46
 
 
47
    # If the selection is already owned, then raise AlreadyOwned rather
 
48
    # than stealing it.
 
49
    IF_UNOWNED = "if_unowned"
 
50
    # If the selection is already owned, then steal it, and then block until
 
51
    # the previous owner has signaled that they are done cleaning up.
 
52
    FORCE = "force"
 
53
    # If the selection is already owned, then steal it and return immediately.
 
54
    # Created for the use of tests.
 
55
    FORCE_AND_RETURN = "force_and_return"
 
56
    def acquire(self, when):
 
57
        old_owner = self._owner()
 
58
        if when is self.IF_UNOWNED and old_owner != const["XNone"]:
 
59
            raise AlreadyOwned
 
60
 
 
61
        self.clipboard.set_with_data([("VERSION", 0, 0)],
 
62
                                     self._get,
 
63
                                     self._clear,
 
64
                                     None)
 
65
 
 
66
        # Having acquired the selection, we have to announce our existence
 
67
        # (ICCCM 2.8, still).  The details here probably don't matter too
 
68
        # much; I've never heard of an app that cares about these messages,
 
69
        # and metacity actually gets the format wrong in several ways (no
 
70
        # MANAGER or owner_window atoms).  But might as well get it as right
 
71
        # as possible.
 
72
 
 
73
        # To announce our existence, we need:
 
74
        #   -- the timestamp we arrived at
 
75
        #   -- the manager selection atom
 
76
        #   -- the window that registered the selection
 
77
        # Of course, because Gtk is doing so much magic for us, we have to do
 
78
        # some weird tricks to get at these.
 
79
 
 
80
        # Ask ourselves when we acquired the selection:
 
81
        ts_data = self.clipboard.wait_for_contents("TIMESTAMP").data
 
82
        ts_num = unpack("@i", ts_data[:4])[0]
 
83
        # Calculate the X atom for this selection:
 
84
        selection_xatom = get_xatom(self.clipboard, self.atom)
 
85
        # Ask X what window we used:
 
86
        owner_window = myGetSelectionOwner(self.clipboard, self.atom)
 
87
        
 
88
        root = self.clipboard.get_display().get_default_screen().get_root_window()
 
89
        sendClientMessage(root, False, const["StructureNotifyMask"],
 
90
                          "MANAGER",
 
91
                          ts_num, selection_xatom, owner_window, 0, 0)
 
92
 
 
93
        if old_owner != const["XNone"] and when is self.FORCE:
 
94
            # Block in a recursive mainloop until the previous owner has
 
95
            # cleared out.
 
96
            def getwin():
 
97
                window = get_pywindow(self.clipboard, old_owner)
 
98
                window.set_events(window.get_events() | gtk.gdk.STRUCTURE_MASK)
 
99
                return window
 
100
            try:
 
101
                window = trap.call(getwin)
 
102
                log("got window")
 
103
            except XError, e:
 
104
                log("Previous owner is already gone, not blocking")
 
105
            else:
 
106
                log("Waiting for previous owner to exit...")
 
107
                add_event_receiver(window, self)
 
108
                gtk.main()
 
109
                log("...they did.")
 
110
 
 
111
    def do_wimpiggy_destroy_event(self, event):
 
112
        remove_event_receiver(event.window, self)
 
113
        gtk.main_quit()
 
114
 
 
115
    def _get(self, clipboard, outdata, which, userdata):
 
116
        # We are compliant with ICCCM version 2.0 (see section 4.3)
 
117
        outdata.set("INTEGER", 32, pack("@ii", 2, 0))
 
118
 
 
119
    def _clear(self, clipboard, userdata):
 
120
        self.emit("selection-lost")
 
121
 
 
122
gobject.type_register(ManagerSelection)