Check-in [a7356e4e43]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merged from dev1.x to rel1.x in anticipation of 0.2.0 release.
Timelines: family | ancestors | descendants | both | rel1.x
Files: files | file ages | folders
SHA1:a7356e4e43cff37a7288b7524c93342f39c675c1
User & Date: manuv 2013-08-04 09:47:02
Context
2013-08-04
10:44
Updated documentation prior to 0.2.0 release. check-in: 6c304fa76f user: mvnathan tags: rel1.x
09:47
Merged from dev1.x to rel1.x in anticipation of 0.2.0 release. check-in: a7356e4e43 user: manuv tags: rel1.x
09:28
Added rectangle to minx.layout.full constructor. Leaf check-in: 49f4a20229 user: manuv tags: dev1.x
2013-04-14
12:48
Released version 0.1.9, which has support for forcibly killing applications that claim to support the WM_DELETE_WINDOW protocol but botch it. check-in: 797b95bcc3 user: mvnathan tags: rel1.x, v0.1.9
Changes

Changes to core/config.py.

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
247
248
249
250
251
252
253

254
255
256
257
        @li <tt>terminal</tt> <b>(string)</b> [<em>xterm</em>]:
            This field specifies the command to use for launching a
            terminal window. Your PATH variable will be searched to find
            it; so you don't have to specify a full path. Moreover, you
            can supply command-line arguments for the terminal program
            in this setting and they will be passed as-is. Minx's
            default configuration defines a
            <a href="../wiki/hooks-list.wiki#launch_terminal>key binding</a> 
            that uses the value of this setting.
        @li <tt>synchronize_xlib</tt> <b>(Boolean)</b> [<em>False</em>]:
            If set to <tt>True</tt>, Xlib calls will be synchronous;
            otherwise, the X protocol operates asynchronously.
            Synchronous mode can be useful for debugging.

    """
................................................................................

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #

# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################







|







 







>




114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
247
248
249
250
251
252
253
254
255
256
257
258
        @li <tt>terminal</tt> <b>(string)</b> [<em>xterm</em>]:
            This field specifies the command to use for launching a
            terminal window. Your PATH variable will be searched to find
            it; so you don't have to specify a full path. Moreover, you
            can supply command-line arguments for the terminal program
            in this setting and they will be passed as-is. Minx's
            default configuration defines a
            <a href="../wiki/hooks-list.wiki#launch_terminal>key binding</a>
            that uses the value of this setting.
        @li <tt>synchronize_xlib</tt> <b>(Boolean)</b> [<em>False</em>]:
            If set to <tt>True</tt>, Xlib calls will be synchronous;
            otherwise, the X protocol operates asynchronously.
            Synchronous mode can be useful for debugging.

    """
................................................................................

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Changes to core/focus_list.py.

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193

194
195

196
197
198
199
200


201


class focus_list:
    """A doubly-linked circular list for keeping track of windows that
    can be focused.

    This class implements a doubly-linked circular list that is meant to
    be used by @ref minx.core.wm.wm "the window manager" for keeping
    track of the list of @ref minx.core.window.window "top-level windows"
    that can be focused. The head of this list will always be taken to
    be the currently focused window.

    """

    # Internal class for storing an item and references to its two
    # neighbours.
    class _node:
        def __init__(self, item):
................................................................................
        """
        self._head = None

    # Adding elements to the focus list
    def add(self, item):
        """Insert an item at the beginning of the list.

        @param item The item to be appended.

        This method inserts item (which can be of any type) at the
        beginning of the list, taking care to maintain circularity.

        @note We add new items to the beginning of the list because this
        is usually how we want input focus to behave: when a new window
        is created, it should acquire the input focus.
................................................................................
           self._remove_node(node)

    # Helper function for node removal
    def _remove_node(self, node):
        if node == self._head: # removing list's first element
           if (self._head._left  == self._head and
               self._head._right == self._head): # list has only one element
              self._head._left  = None
              self._head._right = None
              self._head        = None
           else: # removing first element when list has more than one item
              tail = self._head._left
              tail._right = self._head._right
              self._head._right._left = tail
              self._head._left  = None
              self._head._right = None
              self._head = tail._right
        else: # removing an item after the lists' first element
           node._left._right = node._right
           node._right._left = node._left
           node._left  = None
           node._right = None

    # Helper function for finding items in list
    def _find(self, item):
        if self._head == None: # list is empty
           return None         # therefore, it cannot contain item

        if self._head._item == item: # item is list's first element
................................................................................
           return None
        return self._head._item

    def forward(self):
        """Move head to next element.

        This method is meant to allow moving the input focus from the
        current @ref minx.core.window.window "top-level window" to the
        next one in the @ref minx.core.wm.wm "window manager's" list of
        windows that can be focused.

        If the focus list is empty, this function does nothing.

        """
        if self._head != None:
           self._head  = self._head._right

    def backward(self):
        """Move head to previous element.

        This method is meant to allow moving the input focus from the
        current @ref minx.core.window.window "top-level window" to the
        previous one in the @ref minx.core.wm.wm "window manager's" list
        of windows that can be focused.

        If the focus list is empty, this function does nothing.

        """
        if self._head != None:
           self._head  = self._head._left

#-----------------------------------------------------------------------


# Editor config:
#

# Local Variables:
# indent-tabs-mode: nil
# py-indent-offset: 4
# End:
#


# vim: expandtab shiftwidth=4 tabstop=4








|
|
|







 







|







 







|
|
|

|
|
|
|
|
|

|
|
|
|







 







|
|
|











|
|
|









>
|
<
>
|
|
|
<
|
>
>
|
>
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

196
197
198
199

200
201
202
203
204

class focus_list:
    """A doubly-linked circular list for keeping track of windows that
    can be focused.

    This class implements a doubly-linked circular list that is meant to
    be used by @ref minx.core.wm.wm "the window manager" for keeping
    track of the list of @ref minxlib::window "top-level windows" that
    can be focused. The head of this list will always be taken to be the
    currently focused window.

    """

    # Internal class for storing an item and references to its two
    # neighbours.
    class _node:
        def __init__(self, item):
................................................................................
        """
        self._head = None

    # Adding elements to the focus list
    def add(self, item):
        """Insert an item at the beginning of the list.

        @param item The item to be added.

        This method inserts item (which can be of any type) at the
        beginning of the list, taking care to maintain circularity.

        @note We add new items to the beginning of the list because this
        is usually how we want input focus to behave: when a new window
        is created, it should acquire the input focus.
................................................................................
           self._remove_node(node)

    # Helper function for node removal
    def _remove_node(self, node):
        if node == self._head: # removing list's first element
           if (self._head._left  == self._head and
               self._head._right == self._head): # list has only one element
               self._head._left  = None
               self._head._right = None
               self._head        = None
           else: # removing first element when list has more than one item
               tail = self._head._left
               tail._right = self._head._right
               self._head._right._left = tail
               self._head._left  = None
               self._head._right = None
               self._head = tail._right
        else: # removing an item after the lists' first element
            node._left._right = node._right
            node._right._left = node._left
            node._left  = None
            node._right = None

    # Helper function for finding items in list
    def _find(self, item):
        if self._head == None: # list is empty
           return None         # therefore, it cannot contain item

        if self._head._item == item: # item is list's first element
................................................................................
           return None
        return self._head._item

    def forward(self):
        """Move head to next element.

        This method is meant to allow moving the input focus from the
        current @ref minxlib::window "top-level window" to the next one
        in the @ref minx.core.wm.wm "window manager's" list of windows
        that can be focused.

        If the focus list is empty, this function does nothing.

        """
        if self._head != None:
           self._head  = self._head._right

    def backward(self):
        """Move head to previous element.

        This method is meant to allow moving the input focus from the
        current @ref minxlib::window "top-level window" to the previous
        one in the @ref minx.core.wm.wm "window manager's" list of
        windows that can be focused.

        If the focus list is empty, this function does nothing.

        """
        if self._head != None:
           self._head  = self._head._left

#-----------------------------------------------------------------------

##############################################
# Editor config:                             #

##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #

# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Changes to core/hooks.py.

68
69
70
71
72
73
74



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
...
111
112
113
114
115
116
117
118
119
120
121
















































122
123
124
125
126
127
128
129
130
131
















132
133
134
135
136
137
138
...
300
301
302
303
304
305
306










307
308
309
310
311
312
313
...
363
364
365
366
367
368
369










370
371
372
373
374
375
376
...
540
541
542
543
544
545
546


547
548
549


550
551
552
553
554
555


556
557
558


559
560
561
562
563
564
565
...
579
580
581
582
583
584
585

586
587
588
589
    as the keys, in Minx, keys are strings that name the different hooks
    that Minx supports (e.g., 'manage_hook', 'x_create_notify', and so
    on). If you name your hooks using a non-string type, those hooks
    will be added to the map but simply be ignored.

    """




    # Constructor
    def __init__(self):
        """Create an empty hook map.

        @return An empty hook map.

        When a new hooks object is created, it will have no hook
        functions in it. Clients (i.e., other Minx classes and
        functions or end-user configuration code) will have to then add
        hook functions to the hook map.

        @note In general, there should be no need in end-user code to
        create a hooks object. Rather end-users should use the hooks
        attribute of the @ref minx.core.wm.wm "main window manager object".
        Refer to the <a href="../wiki/hooks-howto.wiki">Hooks HOWTO</a>
        for more on typical usage patterns for this class.

        """
        self._hooks = {}

................................................................................
        instance and we don't want duplicate entries of the same
        function in our hook map (because each hook should be called
        once and only once per triggering).

        Priorities should be integers in the range [1, 100]. Higher
        numbers will insert f into the beginning of the list. If the
        priority p is not supplied, it will be assigned a default
        priority. Passing a non-integral value or an out-of-range value
        for the parameter p will also result in the priority for f
        being set to the default priority.

















































        @note As mentioned earlier, although you can use any
        (reasonable) type for the hook map's keys, in Minx, we use
        strings as the keys. That is, pass a string as the first
        parameter k.

        """
        if p == None or not isinstance(p, int) or p < 1 or p > 100:
           p = self.default_priority()
        if k not in self._hooks:
           self._hooks[k] = priority_queue()
















        if self._hooks[k].add(f, p):
           logger.info('added hook: [{}, {}, {}]'.format(k, f.__name__, p))

    # Remove all hooks for specified name
    def remove(self, k):
        """Remove all hooks for specified name.

................................................................................

        The above example is contrived because if that is all your Minx
        start-up file contained, there would be no manage_hook
        installed and, so, no need to check to ensure that
        my_manage_hook() would execute first. Furthermore, usually,
        only a single manage_hook would be needed; there's no point to
        having multiple manage hooks.











        Nonetheless, the example serves to illustrate how and why you
        might want to use the hooks.max_priority() function.

        Note that if the hook map does not contain any hooks for the
        key k, this function will return a negative number.

................................................................................

        The above example is contrived because if that is all your Minx
        start-up file contained, there would be no manage_hook
        installed and, so, no need to check to ensure that
        my_manage_hook() would execute last. Furthermore, usually,
        only a single manage_hook would be needed; there's no point to
        having multiple manage hooks.











        Nonetheless, the example serves to illustrate how and why you
        might want to use the hooks.min_priority() function.

        If the hook map does not contain any hooks for the key k, this
        function will return a negative number.

................................................................................
           return True
        return False # priority queue already contains v

    # Priority of highest-priority item
    def max_priority(self):
        """Return priority of highest-priority item in priority queue.



        """
        # Each item in the priority queue is a 3-tuple, the first element
        # of which is the negated priority (for making a max heap).


        return -self._queue[0][0]

    # Priority of lowest-priority item
    def min_priority(self):
        """Return priority of lowest-priority item in priority queue.



        """
        # Each item in the priority queue is a 3-tuple, the first element
        # of which is the negated priority (for making a max heap).


        return -self._queue[-1][0]

    # Iterator interface
    def __iter__(self):
        """Iterator interface to priority queue.

        This function returns an iterator for accessing the priority
................................................................................

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #

# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################







>
>
>












|







 







|
|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






<
<


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>







 







>
>



>
>






>
>



>
>







 







>




68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
...
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
...
672
673
674
675
676
677
678
679
680
681
682
683
    as the keys, in Minx, keys are strings that name the different hooks
    that Minx supports (e.g., 'manage_hook', 'x_create_notify', and so
    on). If you name your hooks using a non-string type, those hooks
    will be added to the map but simply be ignored.

    """

    MIN_PRIORITY = 0
    MAX_PRIORITY = 101

    # Constructor
    def __init__(self):
        """Create an empty hook map.

        @return An empty hook map.

        When a new hooks object is created, it will have no hook
        functions in it. Clients (i.e., other Minx classes and
        functions or end-user configuration code) will have to then add
        hook functions to the hook map.

        @note In general, there should be no need in end-user code to
        create a hooks object. Rather, end-users should use the hooks
        attribute of the @ref minx.core.wm.wm "main window manager object".
        Refer to the <a href="../wiki/hooks-howto.wiki">Hooks HOWTO</a>
        for more on typical usage patterns for this class.

        """
        self._hooks = {}

................................................................................
        instance and we don't want duplicate entries of the same
        function in our hook map (because each hook should be called
        once and only once per triggering).

        Priorities should be integers in the range [1, 100]. Higher
        numbers will insert f into the beginning of the list. If the
        priority p is not supplied, it will be assigned a default
        priority. Passing a non-integral value for the parameter p will
        also result in the priority for f being set to the default
        priority.

        To add a hook at the highest priority, pass MAX_PRIORITY as the
        value of p. Similarly, to add a hook at the lowest priority, pass
        MIN_PRIORITY as the value of p. Alternatively, to add hooks at
        the highest or lowest priorities, you can pass values greater
        than hundred or less than one respectively (however, using
        MAX_PRIORITY and MIN_PRIORITY is probably clearer).

        Here is a snippet of code illustrating how to add a hook for some
        event 'foo' so that it is the highest priority hook:

        @verbatim
            wm.hooks.add('foo', my_foo_hook, wm.hooks.MAX_PRIORITY)
        @endverbatim

        In general, you should not care about the order in which hooks
        execute. That is, you should implement hook functions so that the
        final result is the same regardless of the order of their
        execution. Having said that, however, such idealism is not always
        feasible, and, depending on the specific situation, perhaps not
        even wholly desirable. Thus, if you do need to control the order
        in which your hooks execute, you should be aware that the order
        in which you add hooks can be significant, especially if you use
        MIN_PRIORITY and MAX_PRIORITY.

        For example, let's say you have two hooks A and B for some key
        "foo" and want to execute A before B and B before every other
        hook for "foo." If you add the hooks in the following order:

        @verbatim
            wm.hooks.add('foo', A, wm.hooks.MAX_PRIORITY)
            wm.hooks.add('foo', B, wm.hooks.MAX_PRIORITY)
        @endverbatim

        B will end up at a higher priority than A and will execute before
        A instead of the other way around. This is because, MIN_PRIORITY
        and MAX_PRIORITY look at the current minimum and maximum
        priorities and are not absolute values. To achieve the effect
        described above, you should first add B and then A.
        Alternatively, you could also do the following:

        @verbatim
            wm.hooks.add('foo', A, wm.hooks.MAX_PRIORITY)
            wm.hooks.add('foo', B, wm.hooks.max_priority('foo'))
        @endverbatim

        The above code will first add A at the current highest priority
        for "foo" and then add B at the same priority as A.

        @note As mentioned earlier, although you can use any
        (reasonable) type for the hook map's keys, in Minx, we use
        strings as the keys. That is, pass a string as the first
        parameter k.

        """


        if k not in self._hooks:
           self._hooks[k] = priority_queue()

        if p == None or not isinstance(p, int):
           p = self.default_priority()
        elif p < 1:
           p = self.min_priority(k)
           if p == -1:
              p = self.default_priority() - 1
           else:
              p = max(1, p - 1)
        elif p > 100:
           p = self.max_priority(k)
           if p == -1:
              p = self.default_priority() + 1
           else:
              p = min(p + 1, 100)

        if self._hooks[k].add(f, p):
           logger.info('added hook: [{}, {}, {}]'.format(k, f.__name__, p))

    # Remove all hooks for specified name
    def remove(self, k):
        """Remove all hooks for specified name.

................................................................................

        The above example is contrived because if that is all your Minx
        start-up file contained, there would be no manage_hook
        installed and, so, no need to check to ensure that
        my_manage_hook() would execute first. Furthermore, usually,
        only a single manage_hook would be needed; there's no point to
        having multiple manage hooks.

        Moreover, to add a hook at the highest priority, you can simply
        pass MAX_PRIORITY to the call to hooks.add() instead of going
        about it yourself. For example, to add the highest priority hook
        for some event 'foo', you could just call the hooks.add() method
        as shown below:

        @verbatim
            wm.hooks.add('foo', my_foo_hook, wm.hooks.MAX_PRIORITY)
        @endverbatim

        Nonetheless, the example serves to illustrate how and why you
        might want to use the hooks.max_priority() function.

        Note that if the hook map does not contain any hooks for the
        key k, this function will return a negative number.

................................................................................

        The above example is contrived because if that is all your Minx
        start-up file contained, there would be no manage_hook
        installed and, so, no need to check to ensure that
        my_manage_hook() would execute last. Furthermore, usually,
        only a single manage_hook would be needed; there's no point to
        having multiple manage hooks.

        Moreover, to add a hook at the lowest priority, you can simply
        pass MIN_PRIORITY to the call to hooks.add() instead of going
        about it yourself. For example, to add the lowest priority hook
        for some event 'foo', you could just call the hooks.add() method
        as shown below:

        @verbatim
            wm.hooks.add('foo', my_foo_hook, wm.hooks.MIN_PRIORITY)
        @endverbatim

        Nonetheless, the example serves to illustrate how and why you
        might want to use the hooks.min_priority() function.

        If the hook map does not contain any hooks for the key k, this
        function will return a negative number.

................................................................................
           return True
        return False # priority queue already contains v

    # Priority of highest-priority item
    def max_priority(self):
        """Return priority of highest-priority item in priority queue.

        If the priority queue is empty, this function will return -1.

        """
        # Each item in the priority queue is a 3-tuple, the first element
        # of which is the negated priority (for making a max heap).
        if len(self._queue) <= 0:
           return -1
        return -self._queue[0][0]

    # Priority of lowest-priority item
    def min_priority(self):
        """Return priority of lowest-priority item in priority queue.

        If the priority queue is empty, this function will return -1.

        """
        # Each item in the priority queue is a 3-tuple, the first element
        # of which is the negated priority (for making a max heap).
        if len(self._queue) <= 0:
           return -1
        return -self._queue[-1][0]

    # Iterator interface
    def __iter__(self):
        """Iterator interface to priority queue.

        This function returns an iterator for accessing the priority
................................................................................

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Added core/layman.py.



































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
##
# @file  layman.py
# @brief Class to manage layouts.
# @defgroup grp_minx_core_layman Layout Manager
#

#
# Copyright (C) 2012 The Minx Project Developers
#
# See wiki/copyright.wiki in the top-level directory of the Minx source
# distribution for the full list of authors who have contributed to this
# project.
#

#
# This file is part of Minx.
#
# Minx is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# Minx is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Minx. If not, see <http://www.gnu.org/licenses/>.
#

#-------------------------- MODULE DOC STRING ---------------------------

"""@ingroup grp_minx_core_layman
A helper for managing layouts.

This module defines helper classes for dealing with different layouts.
The layouts member of the @ref minx.core.wm.wm "main window manager object"
is an instance of the layout manager class. Both the main window manager
object as well as end-user code will use the layouts object to interface
with the different layout objects that Minx currently has.

"""

#------------------------------- IMPORTS --------------------------------

# Standard library
import logging

# Minx
from minx import layout

#---------------------------- MODULE LOGGER -----------------------------

logger = logging.getLogger(__name__)

#-------------------------------- CLASS ---------------------------------

class layman:
    """@ingroup grp_minx_core_layman
    Layout manager.

    This class keeps track of the different layout objects created by
    end-users and also provides a convenient API for dealing with these
    layouts.

    """

    # Constructor
    def __init__(self, wm):
        """Construct layout manager.

        @param wm The @ref minx.core.wm.wm "main window manager object".

        The layout manager is meant to be used by the
        @ref minx.core.wm.wm "main window manager object". When the
        layout manager is created, we squirrel away a reference to the
        main wm object so we can later get access to the "global"
        @ref minx.core.config.config "config" object and any other things
        we might need from the wm object.

        """
        self._wm = wm
        self._layouts = []

    # Add a layout object to internal list
    def add(self, layout):
        """Add given layout object to the layout manager.

        @param layout The layout object to be added.

        This method adds the specified layout object to the layout
        manager's internal list and performs other necessary bookkeeping.

        """
        self._layouts.append(layout)

    # Find layout corresponding to specified window or name
    def find(self, w):
        """Return layout object given either its X window or the window's name.

        @param  w A string or the @ref minxlib::window "minxlib.window" to find.
        @return Layout matching w.

        This method searches all the layouts currently being managed by
        the layout manager to see if any of them corresponds to the
        window w. If w is a string, then this method will look for a
        layout whose X window has the string w in its name.

        If there is no matching layout, this function will raise an
        unknown_layout exception.

        """
        if isinstance(w, str):
           logger.debug('looking for layout with "{}" in its name'.
                        format(w))
           for layout in self._layouts:
               name = layout.window.properties()['name']
               if w in name:
                  return layout
        else: # assume w refers to a minxlib.window
           logger.debug('looking for layout corresponding to window ID {}'.
                        format(w.id))
           for layout in self._layouts:
               if layout.window == w:
                  return layout
        raise unknown_layout(w)

    # Return layout with window that has the input focus
    def focused_layout(self):
        """Return current layout.

        This method returns the layout that has the top-level window that
        currently has the input focus. If no window has the input focus
        or if there is no layout corresponding to the focused window,
        this method will return None.

        """
        logger.debug('looking for focused layout')
        w = self._wm.display.get_focused_window()
        logger.debug('focused window = {}'.format(w.id))
        if w.id == 0:
           logger.debug('no window currently focused')
           return None

        try:
            p = w.parent()
            logger.debug('focused window = {}, its parent = {}'.
                         format(w.id, p.id))
            return self.find(p)
        except unknown_layout:
            logger.debug('no layout corresponding to focused window {}'.
                         format(w.id))
            return None

    # Find layout for new window
    def receptive_layout(self, w):
        """Find a layout to manage a new window.

        @param w The @ref minxlib::window "minxlib.window" to be managed.

        When a new window is created, Minx has to find a layout that will
        manage it. For lack of a better term, we refer to this layout as
        the receptive layout (because it will "receive" the new window).
        To find a layout willing and able to manage the new window, this
        function follows the procedure outlined below:

            @li Trigger the <tt>receptive_layout_hook</tt>.
            @li Failing that, check the focused layout.
            @li Failing that, search the remaining layouts.
            @li Failing that, create a new default layout.

        The <tt>receptive_layout_hook</tt> gives end-user code an
        opportunity to fine-tune the layout to be used for each window.
        If you have multiple functions for this hook, only the return
        value of the first one will be considered. That is, use only one
        hook for this; in fact, you shouldn't need an entire chain of
        hooks for the <tt>receptive_layout_hook</tt>.

        Minx will only accept the return value of the
        <tt>receptive_layout_hook</tt> if all of the following conditions
        are true:

            @li The return value is not None.
            @li It is an instance of the
                @ref minx.layout.base.base "minx.layout.base" class.
            @li The layout is on the same screen as w.
            @li The layout is willing to manage w.

        If the above-mentioned hook is not defined or if it returns
        something not fulfilling the above conditions, we check if the
        layout with the window that is currently focused is on the same
        screen as the new window and is willing to manage it. If so, this
        function will return the currently focused layout as the
        receptive layout, i.e., the layout that will manage the new
        window.

        If no window has the input focus, or if the focused layout is not
        on the same screen as the new window, or if it is unwilling to
        manage the new window, then we search the layout manager's
        remaining layouts for a layout that is on the same screen as the
        new window and is willing to manage it.

        If no such layout exists, Minx will fall back to creating a new
        layout object on the same screen as the new window and use that
        as the receptive layout. The layout class used for this fallback
        policy is referred to as the default layout. Currently, Minx uses
        the @ref minx.layout.full.full "full layout" as the default
        layout class.

        @note End-user code should not need to call this function.
        Specifically, don't call this function from your
        <tt>receptive_layout_hook</tt>.

        """
        s = w.screen()
        logger.debug('looking for receptive layout for window {} on screen {}'.
                     format(w.id, s))

        logger.debug('looking for receptive layout via user hook')
        U = self._wm.hooks.trigger('receptive_layout_hook', w)
        if len(U) > 0:
           logger.info('receptive_layout_hook returned {}'.format(U[0]))
           if (U[0] != None and isinstance(U[0], layout.base) and
               U[0].window.screen() == s and U[0].will_manage(w)):
               if not U[0] in self._layouts:
                  self.add(U[0])
               return U[0] # use first layout returned by hook
           else:
               logger.warning('bad layout ({}) '.format(U[0]) +
                              'returned by receptive_layout_hook')

        F = self.focused_layout()
        logger.debug('checking focused layout {} for receptivity'.format(F))
        if F != None and F.window.screen() == s and F.will_manage(w):
           logger.info('focused layout {} can and will manage window {}'.
                       format(F, w.id))
           return F

        logger.debug('checking remaining layouts for receptivity')
        for L in self._layouts:
            if L == F:
               continue
            if L.window.screen() == s and L.will_manage(w):
               logger.info('layout {} can and will manage window {}'.
                           format(L, w.id))
               return L

        logger.debug('no receptive layout found')
        D = layout.full(self._wm, self._wm.root_windows[s])
        self.add(D)
        logger.info('falling back to default layout {} for window {}'.
                    format(D, w.id))
        return D

#----------------------------- EXCEPTIONS -------------------------------

class unknown_layout(Exception):
    """@ingroup grp_minx_core_layman
    Unknown layout exception.

    The layout manager raises this exception when it fails to find a
    layout object corresponding to some window.

    """
    def __init__(self, w):
        """Construct an unknown_layout.

        @param w The @ref minxlib::window "minxlib.window" or window name
        for which there is no corresponding layout.

        The parameter w can be accessed as the exception's window member.

        """
        self.window = w

#------------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Changes to core/window.py.

1
2
3
4
5
6
7
8
9
10
11
..
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308

309
310



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350

351
352
353
354
##
# @file  window.py
# @brief Minx's encapsulation of top-level windows
# @defgroup grp_minx_core_window Top-level Windows
#

#
# Copyright (C) 2012 The Minx Project Developers
#
# See wiki/copyright.wiki in the top-level directory of the Minx source
# distribution for the full list of authors who have contributed to this
................................................................................
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Minx. If not, see <http://www.gnu.org/licenses/>.
#

#------------------------ MODULE DOC STRING ----------------------------

"""@ingroup grp_minx_core_window
Encapsulation of top-level windows.

This module defines the minx.core.window.window class and related
functions, which are used to encapsulate the details of top-level X
windows.



"""

#----------------------------- IMPORTS ---------------------------------

# Standard library
import logging

# minx
import minxlib

#-------------------------- MODULE LOGGER ------------------------------

logger = logging.getLogger(__name__)

#---------------------- TOP-LEVEL WINDOWS LIST -------------------------

# We use this map to keep track of all the extant top-level windows.
# Other modules should consider this private and not use it directly.
_top_level_windows = {}

# Check if top-level window exists
def exists(w):
    """@ingroup grp_minx_core_window
    See if window is present in list of top-level windows.

    @param  w ID of top-level window whose existence we want to ascertain.
    @return True if w is an extant top-level window; false otherwise.

    Whenever a new minx.core.window.window object is created, the
    minx.core.window module will add it to an internal list. This
    function checks this internal list to see if the window identified
    by w corresponds to an extant top-level window.

    @note w may either be an instance of minxlib.window or an integer X
    window ID.

    @par
    Although we refer to it as a list, the internal list mentioned above
    need not be an actual list. That is, don't make any assumptions of
    the minx.core.window module's internal data structures and use only
    its public interface.

    """
    if isinstance(w, minxlib.window):
       return w.id in _top_level_windows
    if isinstance(w, int):
       return w in _top_level_windows
    return False # should raise an exception maybe?

# Retrieve top-level window
def get(w):

    """@ingroup grp_minx_core_window
    Retrieve top-level window object from internal list.


    @param  w The minxlib window or X ID of the window we want.
    @return minx.core.window.window object corresponding to w or None.

    Whenever a new minx.core.window.window object is created, the
    minx.core.window module will add it to an internal list. This
    retrieves the minx.core.window.window object from the
    above-mentioned internal list given either the minxlib.window object
    for the desired window or its X resource ID.

    If there is no top-level window object corresponding to w stored by
    this module, this function will return None.

    @note This function does not first check to ensure that w is a
    valid and extant window. You <b>must</b> call the window.exists()
    function before calling window.get() to ensure that the call to
    get() will actually work. Otherwise, this function will fail.

    """
    if isinstance(w, minxlib.window):
       return _top_level_windows[w.id]
    if isinstance(w, int):
       return _top_level_windows[w]
    return None # should raise an exception maybe?

#------------------------- X EVENT HANDLERS ----------------------------

# What to do when a top-level window is successfully configured: simply
# set its internal flag indicating this.
def _on_configure(e):
    if exists(e.target):
       get(e.target)._configured = True
       logger.info('configured top-level window {}'.format(e.target.id))

# What to do when a top-level window has to be shown: if it hasn't been
# configured at least once, first configure it, then show it. This is
# necessary for applications such as xmessage that map windows without
# ever generating a configure request.
#
# OTOH it seems to mess up other applications (e.g., the XFCE Terminal
# program seems to end up ignoring its --geometry specifications). Oh
# well, we'll have to rethink all of this anyway when we get to
# layouts... So no point sweating it now.
def _on_map_request(e):
    if exists (e.target):
       w = get(e.target)
       if not w._configured:
          logger.info('need to configure window {}'.format(e.target.id))
          w.configure()
       logger.info('mapping window {}'.format(e.target.id))
       w._window.show()

# When a top-level window is destroyed, remove it from the internal list
# of top-level windows.
def _on_destroy(e):
    if exists(e.target):
       logger.info('deleting window {}'.format(e.target.id))
       del _top_level_windows[e.target.id]

#-------------------- TOP-LEVEL WINDOW ENCAPSULATION -------------------

class window:
    """@ingroup grp_minx_core_window
    Encapsulation of top-level X windows.

    This class wraps around @ref minxlib::window "minxlib.window" in
    order to keep track of top-level windows. Unlike minxlib.window,
    which can represent any X window, this class only represents
    top-level windows.

    """

    # Top-level window construction
    def __init__(self, m, w, x, y, width, height):
        """Create a top-level window object.

        @param m The main window manager object.
        @param w The minxlib.window object representing the top-level window.
        @param x The x-coordinate of the window's origin.
        @param y The y-coordinate of the window's origin.
        @param width  The window's width.
        @param height The window's height.


        Minx does not create any windows. It only keeps track of
        windows. Thus, when a window object is instantiated, we need to
        know its corresponding minxlib.window instance.

        The window's geometry will usually be gleaned from the window
        creation notification event. In fact, instances of this class
        are meant to be created in response to the X server's
        CreateNotify event.

        As part of window initialization, this function registers some
        callbacks with the @ref minx.core.wm.wm "window manager's" hooks
        list to be able to respond to relevant X events. It then
        registers the newly created window object in an internal list
        used to track extant top-level windows.

        """
        logger.info('creating top-level window {}'.format(w.id))

        self._wm     = m
        self._window = w
        self._x      = x
        self._y      = y
        self._width  = width
        self._height = height
        self._configured = False

        logger.debug('setting up X handlers for window {}'.format(w.id))
        h = self._wm.hooks
        h.add('x_configure_notify' , _on_configure  )
        h.add('x_map_request',       _on_map_request)
        h.add('x_destroy_notify',    _on_destroy)

        global _top_level_windows
        _top_level_windows[self] = self
        logger.debug('registered window {}'.format(w.id))

    # Equality operator
    def __eq__(self, other):
        """Check if two top-level window objects refer to the same window.

        @param  self  This object.
        @param  other Object against which we would like to compare this one.
        @return True if self and other are the same window; false otherwise.

        This function checks this top-level window object against
        another to see if the two refer to the same underlying X window.
        The other object may be one of the following types:

            @li Another minx.core.window.window instance;
            @li A minxlib.window instance;
            @li An integer X window ID.

        This function relies on the equality operators implemented by
        minxlib.window.

        """

        # If we're dealing with another minx.core.window, compare the
        # two underlying minxlib.window instances inside the
        # minx.core.window objects.
        if isinstance(other, window):
           return self._window == other._window

        # If we're given a minxlib.window or an integer, use the
        # minxlib::window equality operator that works with Xlib Window
        # ID's (see minxlib/window.hh).
        if isinstance(other, minxlib.window) or isinstance(other, int):
           return self._window == other

        # We got neither a minx.core.window, nor a minxlib.window, nor
        # an Xlib Window ID ==> can't compare, i.e., this object is
        # definitely not equal to the other one.
        return False

    # Inequality operator
    def __ne__(self, other):
        """Check if two top-level window objects refer to different windows.

        @param  self  This object.
        @param  other Object against which we would like to compare this one.
        @return True if self and other are different windows; false otherwise.

        This function checks this top-level window object against
        another to see if the two refer to different underlying X
        windows. This function is simply a negation of the __eq__()
        operator.

        """
        return not (self == other)

    # Keys for use with associative containers
    def __hash__(self):
        """Return an integral ID that can be used as a key.

        This function allows us to store top-level window objects in
        sets, dictionaries, and other such types.

        """
        return self._window.id

    # HACK! Will eventually go away once layouts enter the scene...
    def configure(self):
        """Set top-level window geometry.

        This method moves and resizes the top-level window using the
        geometry settings given during instantiation.

        """
        self._window.move_resize(self._x, self._y,
                                 self._width, self._height)

    # Reset border color and size to match focused window and then focus
    # the window.
    def focus(self):
        """Focus this window.

        This method sets the window's border attributes to those of the
        active window and then raises and focuses the window.

        """
        c = self._wm.config
        logger.info('setting window {} border attributes: 0x{:0<6X}, {}'.
                    format(self._window.id,
                           c.active_border_color, c.active_border_size))
        self._window.set_border_attr(c.active_border_color,
                                     c.active_border_size)

        logger.debug('focusing window {}'.format(self._window.id))
        self._window.focus()

    # Reset border color and size to match unfocused windows
    def unfocus(self):

        """Unfocus this window.




        This method sets the window's border attributes to those of
        inactive windows.

        """
        c = self._wm.config
        logger.info('setting window {} border attributes: 0x{:0<6X}, {}'.
                    format(self._window.id,
                           c.inactive_border_color, c.inactive_border_size))
        self._window.set_border_attr(c.inactive_border_color,
                                     c.inactive_border_size)

    # Kill window
    def kill(self):
        """Kill this window.

        This method kills the X client application that created this
        window, thereby, destroying it.

        """
        self._window.kill()

    # Kill window using brute force
    def nuke(self):
        """Kill this window using brute force.

        This method is meant to be used on windows whose X client
        applications advertise support for the WM_DELETE_WINDOW protocol
        but fail to implement it properly.

        """
        self._window.nuke()

#-----------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #

# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################


|
|







 







|


|

|
|
|
>
>



|







|



|

|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>

<
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|

|
<
|
<
|
|
<

|
|

|
|
>
|

>
>
>
|
|

|
<
|
<
|
|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|







>




1
2
3
4
5
6
7
8
9
10
11
..
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

















60
















61
62

63
64












































































65




66
67




















































































































68
69
70
71

72

73
74

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

91

92
93

94





















95
96
97
98
99
100
101
102
103
104
105
106
107
##
# @file  window.py
# @brief Convenience functions for working with windows.
# @defgroup grp_minx_core_window Window Convenience API
#

#
# Copyright (C) 2012 The Minx Project Developers
#
# See wiki/copyright.wiki in the top-level directory of the Minx source
# distribution for the full list of authors who have contributed to this
................................................................................
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Minx. If not, see <http://www.gnu.org/licenses/>.
#

#-------------------------- MODULE DOC STRING ---------------------------

"""@ingroup grp_minx_core_window
Convenience functions for working with windows.

This module defines some convenience functions for dealing with
minxlib.window objects. Although you could simply call minxlib.window
functions directly, the functions defined in this module are more
convenient and also take into account things such as settings in
minx.core.config.

"""

#------------------------------- IMPORTS --------------------------------

# Standard library
import logging

# minx
import minxlib

#---------------------------- MODULE LOGGER -----------------------------

logger = logging.getLogger(__name__)

#------------------------ CONVENIENCE FUNCTIONS -------------------------

# Reset border color and size to match focused window and then focus

















# the window.
















def focus(w, c):
    """@ingroup grp_minx_core_window

    Focus the specified window.













































































    @param w The minxlib.window object to be focused.




    @param c The minx.core.config object containing Minx settings.





















































































































    This function sets the window's border attributes to those of the
    active window and then raises and focuses the window.

    """

    logger.info('setting window {} border attributes: 0x{:0<6X}, {}'.

                format(w.id, c.active_border_color, c.active_border_size))
    w.set_border_attr(c.active_border_color, c.active_border_size)


    logger.debug('focusing window {}'.format(w.id))
    w.focus()

# Reset border color and size to match unfocused windows
def unfocus(w, c):
    """@ingroup grp_minx_core_window
    Unfocus the specified window.

    @param w The minxlib.window object to be unfocused.
    @param c The minx.core.config object containing Minx settings.

    This function sets the window's border attributes to those of
    inactive windows.

    """

    logger.info('setting window {} border attributes: 0x{:0<6X}, {}'.

                format(w.id, c.inactive_border_color, c.inactive_border_size))
    w.set_border_attr(c.inactive_border_color, c.inactive_border_size)























#------------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Changes to core/wm.py.

40
41
42
43
44
45
46
47
48

49
50
51
52
53
54



55
56
57
58
59
60
61
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
...
138
139
140
141
142
143
144






























































145
146
147
148
149
150
151
...
158
159
160
161
162
163
164

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
197
198
199
200
201
202
203













204
205
206
207
208
209
210

211
212
213
214



215
216
217
218
219


220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
...
264
265
266
267
268
269
270

271
272
273
274
275
276
277
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
...
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
395
396
397
398
399
400
401
402
403

404
405
406








407
408
409
410
411
412
413
...
429
430
431
432
433
434
435












436
437
438
439
440
441
442
...
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
...
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
...
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
...
626
627
628
629
630
631
632











633
634
635
636
637
638
639
640
641

642
643
644
645
#----------------------------- IMPORTS ---------------------------------

# Standard library
import shlex, subprocess
import re, logging, logging.config
import sys, traceback

# Minx
from   window     import window

from   xevents    import xevents
from   hooks      import hooks
from   config     import config
from   focus_list import focus_list
import minxlib




#-------------------------- MODULE LOGGER ------------------------------

logger = logging.getLogger(__name__)

#------------------------- CLASS DEFINITION ----------------------------

class wm:
................................................................................
        #!/usr/bin/env python
        import minx
        minx.core.wm().start()
    @endverbatim

    Of course, the above code requires the minx package to be in your
    Python path. But, assuming that is the case, it would start Minx
    with the default keybindings, hooks, and other settings.

    To make simple customizations, create an instance of the
    @ref minx.core.config.config "config" class and pass that to the wm
    constructor:

    @verbatim
        #!/usr/bin/env python
................................................................................
    <a href="../wiki/hooks-list.wiki">Hooks List</a> documents all the
    hooks that Minx currently supports.

    One important use of hooks is for defining custom key bindings. The
    <a href="../wiki/key-bindings.wiki">Key Bindings HOWTO</a> has the
    details.































































    @note Although this class is defined in the minx.core.wm "package"
    and, technically, must be accessed as minx.core.wm.wm, it can, in
    fact (and as illustrated in the code snippets above), be referred to
    simply as minx.core.wm.

    """
    # Constructor
................................................................................
        manager. You will have to first create an instance of this class
        and then call its start() method to run the window manager. This
        constructor simply sets up some internal attributes, the most
        important of which are:

            @li config
            @li hooks


        The @ref minx.core.config.config "config" object specifies
        various settings such as the border colors, sizes, etc. To
        customize these settings, you will (typically) create a config
        object, change its various attributes, and pass that object into
        this constructor. (Alternatively, you could first create a wm
        object and then access its config attribute; however, the former
        approach is the preferred style.)

        If you don't supply a config object, the window manager will use
        default settings.

        The wm object's @ref minx.core.hooks.hooks "hooks" attribute
        maps names of hook functions (i.e., strings) to prioritized
        lists of callables. Minx will trigger these hooks at appropriate
        points in its event processing workflow. Minx uses hooks both
        internally (to handle various events) and "externally" (to allow
        end-users to customize its behaviour).
................................................................................
        them internally, it is easy to override its internal hooks and
        even disable them. Usually, this will cause Bad Things (TM) to
        happen. Thus, you should exercise care when dealing with
        "dangerous" hooks. As long as you stick to "external" hooks used
        by Minx specifically to effect customization, you should be
        okay.














        """
        if conf == None: # create a default config object
           conf =  config()
        self.config = conf

        # Configure Minx's root logger
        logging.config.dictConfig(conf.logger)


        # Other initialization
        logger.debug('creating focus list')
        self._focus_list = focus_list()




        logger.info('setting up X event handlers and default keybindings')
        self.hooks = hooks()
        self._xevents = xevents(self)
        self._init_keybindings()



    # Default keybindings
    def _init_keybindings(self):
        self.hooks.add(  'A-Tab', self.focus_next)
        self.hooks.add('S-A-Tab', self.focus_prev)
        self.hooks.add(   'A-F4', self.kill)
        self.hooks.add(   'C-F4', self.nuke)
        self.hooks.add(  'C-A-T', lambda: self.spawn(self.config.terminal))


    # Start the window manager
    def start(self):
        """Start the window manager.

        Call this method after creating a wm instance to get Minx up and
        running. Here is an example showing how to start Minx with its
................................................................................
        @verbatim
            #!/usr/bin/env python
            import minx
            minx.core.wm().start()
        @endverbatim

        If you would like to customize Minx by specifying your own hooks
        to be able deal with various events, the initialization and
        start-up sequence will be a three step procedure as shown below:

        @verbatim
            #!/usr/bin/env python

            import minx

................................................................................
            # Step three: start the window manager
            wm.start()
        @endverbatim

        """
        self.connect_x()
        self.configure_x()

        self.manage_existing()
        self.event_loop()

    # Connect to the X server
    def connect_x(self):
        """Connect to the X server.

................................................................................
        function.

        """
        try:
            conf = self.config
            logger.info('connecting to X server (synchronous = {})'.
                        format(conf.synchronize_xlib))
            self._display = minxlib.display(sync = conf.synchronize_xlib)
        except minxlib.connection_error as e:
            logger.critical(e)
            raise

    # Configure X to send us events we need to manage windows
    def configure_x(self):
        """Configure X server to send Minx events it needs to manage windows.
................................................................................
        If you decide not to use wm.start(), look at the Minx code to
        see how and what you will need to do to effectively use this
        function.

        """
        # Find hooks that correspond to key bindings
        logger.info('finding key binding hooks')
        km = '|'.join(self._display.get_keyboard_mapping())
        kb = re.compile('^(([CAS]|M[1-5]?)-)*(' + km + ')$')
        key_bindings = [n for n in self.hooks.names() if kb.search(n)]
        #logger.debug('km = {}'.format(km))
        logger.debug('key bindings hooks = {}'.format(' '.join(key_bindings)))

        # Setup event masks and passive grabs for all screens
        self._root_windows = self._display.get_root_windows()
        for w in self._root_windows:
            logger.info('configuring root window {}'.format(w.id))
            w.select_events(minxlib.substructure_redirect_mask |
                            minxlib.substructure_notify_mask   |
                            minxlib.key_press_mask)
            for k in key_bindings:
                w.grab_key(k)

................................................................................
        @par
        If you decide not to use wm.start(), look at the Minx code to
        see how and what you will need to do to effectively use this
        function.

        """
        logger.info('managing existing top-level windows')
        for w in self._display.get_top_level_windows():
            if self.manage(w):
               x, y, width, height = w.geometry()
               t = window(self, w, x, y, width, height)
               t._configured = True
               if w.is_mapped():
                  self.add_to_focus_list(t)

    # Event loop
    def event_loop(self):
        """Retrieve and process X events.

        This method implements Minx's event loop. Once we enter this
        function, Minx will initiate an infinite loop, wherein it
................................................................................
        @par
        If you decide not to use wm.start(), look at the Minx code to
        see how and what you will need to do to effectively use this
        function.

        """
        logger.info('entering event loop')
        while True:
            try:

                e = self._display.get_event()
                logger.debug('got event {}'.format(e))
                self.hooks.trigger(str(e), e)









            # Tried to focus a non-existent window (happens sometimes
            # upon window destruction, causing the resulting unmap and
            # focus notification handlers to go out-of-sync with the X
            # server's internal state). We can use the protocol error
            # that ensues as an opportunity to sync the internal states
            # of the window manager and X server.
................................................................................
            # stabilized enough for us to be absolutely sure this bit of
            # code is no longer required.
            except minxlib.change_window_attributes as e:
                logger.warning(e)
                if e.error_code == minxlib.bad_window:
                   pass # ignore protocol error (should be okay)














    # API to focus next window
    def focus_next(self):
        """Focus next window.

        This function passes input focus to the next window in the
        window manager's list of top-level windows that can accept input
................................................................................
        """
        L = self._focus_list
        old_head = L.head()
        L.forward()
        new_head = L.head()
        if new_head != old_head:
           logger.info('switching focus from {} to {}'.
                       format(old_head._window.id, new_head._window.id))
           old_head.unfocus()
           new_head.focus()
        #else: new_head == old_head ==> focus list empty or only one window

    # API to focus previous window
    def focus_prev(self):
        """Focus previous window.

        This function passes input focus to the previous window in the
................................................................................
        """
        L = self._focus_list
        old_head = L.head()
        L.backward()
        new_head = L.head()
        if new_head != old_head:
           logger.info('switching focus from {} to {}'.
                       format(old_head._window.id, new_head._window.id))
           old_head.unfocus()
           new_head.focus()
        #else: new_head == old_head ==> focus list empty or only one window

    # Kill a window
    def kill(self, w = None):
        """Kill window.

        @param w The window to be killed.

        This function kills the X client application that created the
        window w and all of its other windows and X resources. If w is
        not supplied by the caller, this function will kill the
        currently focused window.

        """
................................................................................
        if w != None: # there is a focused window to kill
           w.kill()

    # Kill a window using brute force
    def nuke(self, w = None):
        """Kill window using brute force.

        @param w The window to be nuked.

        This function kills the X client application that created the
        window w and all of its other windows and X resources. If w is
        not supplied by the caller, this function will kill the
        currently focused window.

        @note Whereas the kill() function attempts a graceful shutdown,
................................................................................
                           format(sys.exc_info()[0], cmd))
            logger.debug(traceback.format_exc())

    # Add newly created top-level window to focus list
    def add_to_focus_list(self, w):
        """Add a newly created top-level window to the focus list.

        @param w The minx.core.window.window wrapper object
        encapsulating the top-level window.

        This function is meant to be used internally by Minx.
        Specifically, the wm constructor calls it to take over
        management of any top-level windows that might have been created
        before Minx was started. Also, the <tt>MapNotify</tt> handler in
        the xevents class uses it to add a newly mapped top-level window
        to the focus list and to focus it.
................................................................................

        End-users should not use this function.

        """
        L = self._focus_list
        f = L.head()
        if f != None:
           logger.debug('unfocusing {}'.format(f._window.id))
           f.unfocus()
        logger.debug('adding {} to focus list and focusing'.
                     format(w._window.id))
        L.add(w)
        w.focus()

    # Whether or not manage the given window
    def manage(self, w):
        """Check if given window should be managed or not.

        @param  w The @ref minxlib::window "minxlib.window" to be checked.
        @return True if Minx should manage w, false if it should ignore w.
................................................................................
        for r in self.hooks.trigger('manage_hook', w):
            if r == False:  # some manage hook returned False
               return False # therefore, ignore w

        # If we get here, all manage hooks passed w ==> we should
        # manage it...
        return True












#-----------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #

# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################







|
|
>






>
>
>







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>






|
<
<
|
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>







>




>
>
>





>
>








>







 







|







 







>







 







|







 







|






|
|







 







|
|
|
|
|
|
<







 







|

>
|


>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
|







 







|
|
|






|







 







|







 







|
<







 







|
|
|
<

|







 







>
>
>
>
>
>
>
>
>
>
>









>




40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
..
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238


239

240
241
242
243
244
245
246
...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
...
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
...
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
...
447
448
449
450
451
452
453
454
455
456
457
458
459

460
461
462
463
464
465
466
...
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
...
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
...
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
...
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
...
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
...
677
678
679
680
681
682
683
684

685
686
687
688
689
690
691
...
692
693
694
695
696
697
698
699
700
701

702
703
704
705
706
707
708
709
710
...
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
#----------------------------- IMPORTS ---------------------------------

# Standard library
import shlex, subprocess
import re, logging, logging.config
import sys, traceback

# Minx core
import window
from   layman     import layman
from   xevents    import xevents
from   hooks      import hooks
from   config     import config
from   focus_list import focus_list
import minxlib

# Minx layouts
from minx import layout

#-------------------------- MODULE LOGGER ------------------------------

logger = logging.getLogger(__name__)

#------------------------- CLASS DEFINITION ----------------------------

class wm:
................................................................................
        #!/usr/bin/env python
        import minx
        minx.core.wm().start()
    @endverbatim

    Of course, the above code requires the minx package to be in your
    Python path. But, assuming that is the case, it would start Minx
    with the default key bindings, hooks, and other settings.

    To make simple customizations, create an instance of the
    @ref minx.core.config.config "config" class and pass that to the wm
    constructor:

    @verbatim
        #!/usr/bin/env python
................................................................................
    <a href="../wiki/hooks-list.wiki">Hooks List</a> documents all the
    hooks that Minx currently supports.

    One important use of hooks is for defining custom key bindings. The
    <a href="../wiki/key-bindings.wiki">Key Bindings HOWTO</a> has the
    details.

    To customize how Minx arranges windows, you can:

        @li Specify the layouts you would like to use
        @li Specify the layout for particular windows
        @li Implement your own layouts

    Here is an example that illustrates the first two of the above
    possibilities:

    @verbatim
        #!/usr/bin/env python

        import minx

        def my_init_hook(m):
            scr = m.root_windows
            m.layouts.add(minx.layout.tall(m, scr[0]))
            m.layouts.add(minx.layout.rows(m, scr[1]))

        def my_receptive_layout_hook(w):
            prop = w.properties()
            if 'gimp' in prop['class'].lower():
                global wm
                try:
                    L = wm.layouts.find('gimp')
                    return L
                except minx.core.layman.unknown_layout:
                   parent = wm.root_windows[w.screen()]
                   return minx.layout.gimp(wm, parent)
            return None

        wm = minx.core.wm()
        wm.hooks.add('init_hook', my_init_hook)
        wm.hooks.add('receptive_layout_hook', my_receptive_layout_hook)
        wm.start()
    @endverbatim

    The <a href="../wiki/hooks-list.wiki#init_hook">init hook</a> allows
    you to setup the layouts you want to use. If you don't supply an init
    hook, Minx will use a default layout for each screen. At this time,
    the default layout is the @ref minx.layout.full.full "full" layout,
    which shows one window at a time by resizing windows to occupy the
    entire screen. In the above example, we use the tall layout for the
    first screen and the rows layout for the second screen. Of course,
    this would only work if you actually have two screens. Please also
    note that the tall and rows layout classes have not yet been
    implemented; we use these nonexistent layouts just to illustrate the
    intended spirit of layouts specification.

    The <a href="../wiki/hooks-list.wiki#receptive_layout_hook">receptive
    layout hook</a> allows you to either create or find a layout for each
    new window that Minx manages. In the above example, we created an
    instance of the gimp layout when Gimp starts up and use that for
    subsequent windows. For all other window types, we fall back to
    either the tall or rows layouts setup in the init hook. If, for some
    reason, the tall and rows layouts refuse to manage the new window,
    Minx will fall back its default, viz., the full layout. Again, like
    tall and rows, the gimp layout does not exist yet; it was used merely
    to illustrate how the receptive layout hook is intended to work.

    Documentation for implementing layouts will come later.

    @note Although this class is defined in the minx.core.wm "package"
    and, technically, must be accessed as minx.core.wm.wm, it can, in
    fact (and as illustrated in the code snippets above), be referred to
    simply as minx.core.wm.

    """
    # Constructor
................................................................................
        manager. You will have to first create an instance of this class
        and then call its start() method to run the window manager. This
        constructor simply sets up some internal attributes, the most
        important of which are:

            @li config
            @li hooks
            @li layouts

        The @ref minx.core.config.config "config" object specifies
        various settings such as the border colors, sizes, etc. To
        customize these settings, you will (typically) create a config
        object, change its various attributes, and pass that object into
        this constructor. (Alternatively, you could first create a wm
        object and then access its config attribute.) If you don't supply


        a config object, the window manager will use default settings.


        The wm object's @ref minx.core.hooks.hooks "hooks" attribute
        maps names of hook functions (i.e., strings) to prioritized
        lists of callables. Minx will trigger these hooks at appropriate
        points in its event processing workflow. Minx uses hooks both
        internally (to handle various events) and "externally" (to allow
        end-users to customize its behaviour).
................................................................................
        them internally, it is easy to override its internal hooks and
        even disable them. Usually, this will cause Bad Things (TM) to
        happen. Thus, you should exercise care when dealing with
        "dangerous" hooks. As long as you stick to "external" hooks used
        by Minx specifically to effect customization, you should be
        okay.

        Finally, the @ref minx.core.layman.layman "layouts" object
        provides an interface for dealing with window layouts. By
        default, Minx uses the @ref minx.layout.full.full "full" layout,
        which shows one window at a time, resizing windows so that they
        occupy the entire screen. You can use the
        <a href="../wiki/hooks-list.wiki#init_hook">init hook</a> to add
        your preferred layouts to the layout manager.

        Another way to customize layout selection is to use the
        <a href="../wiki/hooks-list.wiki#receptive_layout_hook">receptive
        layout hook</a>, which provides a mechanism for deciding a layout
        for each new window.

        """
        if conf == None: # create a default config object
           conf =  config()
        self.config = conf

        # Configure Minx's root logger
        logging.config.dictConfig(conf.logger)
        logger.info('starting Minx')

        # Other initialization
        logger.debug('creating focus list')
        self._focus_list = focus_list()

        logger.debug('creating layout manager')
        self.layouts = layman(self)

        logger.info('setting up X event handlers and default keybindings')
        self.hooks = hooks()
        self._xevents = xevents(self)
        self._init_keybindings()

        self._quit = False

    # Default keybindings
    def _init_keybindings(self):
        self.hooks.add(  'A-Tab', self.focus_next)
        self.hooks.add('S-A-Tab', self.focus_prev)
        self.hooks.add(   'A-F4', self.kill)
        self.hooks.add(   'C-F4', self.nuke)
        self.hooks.add(  'C-A-T', lambda: self.spawn(self.config.terminal))
        self.hooks.add(  'C-A-X', self.quit)

    # Start the window manager
    def start(self):
        """Start the window manager.

        Call this method after creating a wm instance to get Minx up and
        running. Here is an example showing how to start Minx with its
................................................................................
        @verbatim
            #!/usr/bin/env python
            import minx
            minx.core.wm().start()
        @endverbatim

        If you would like to customize Minx by specifying your own hooks
        to be able to deal with various events, the initialization and
        start-up sequence will be a three step procedure as shown below:

        @verbatim
            #!/usr/bin/env python

            import minx

................................................................................
            # Step three: start the window manager
            wm.start()
        @endverbatim

        """
        self.connect_x()
        self.configure_x()
        self.hooks.trigger('init_hook', self)
        self.manage_existing()
        self.event_loop()

    # Connect to the X server
    def connect_x(self):
        """Connect to the X server.

................................................................................
        function.

        """
        try:
            conf = self.config
            logger.info('connecting to X server (synchronous = {})'.
                        format(conf.synchronize_xlib))
            self.display = minxlib.display(sync = conf.synchronize_xlib)
        except minxlib.connection_error as e:
            logger.critical(e)
            raise

    # Configure X to send us events we need to manage windows
    def configure_x(self):
        """Configure X server to send Minx events it needs to manage windows.
................................................................................
        If you decide not to use wm.start(), look at the Minx code to
        see how and what you will need to do to effectively use this
        function.

        """
        # Find hooks that correspond to key bindings
        logger.info('finding key binding hooks')
        km = '|'.join(self.display.get_keyboard_mapping())
        kb = re.compile('^(([CAS]|M[1-5]?)-)*(' + km + ')$')
        key_bindings = [n for n in self.hooks.names() if kb.search(n)]
        #logger.debug('km = {}'.format(km))
        logger.debug('key bindings hooks = {}'.format(' '.join(key_bindings)))

        # Setup event masks and passive grabs for all screens
        self.root_windows = self.display.get_root_windows()
        for w in self.root_windows:
            logger.info('configuring root window {}'.format(w.id))
            w.select_events(minxlib.substructure_redirect_mask |
                            minxlib.substructure_notify_mask   |
                            minxlib.key_press_mask)
            for k in key_bindings:
                w.grab_key(k)

................................................................................
        @par
        If you decide not to use wm.start(), look at the Minx code to
        see how and what you will need to do to effectively use this
        function.

        """
        logger.info('managing existing top-level windows')
        for w in self.display.get_top_level_windows():
            prop = w.properties()
            logger.debug('window {} class = {}, name = {}'.
                         format(w.id, prop['class'], prop['name']))
            if prop['class'] != 'minx.layout' and self.manage(w):
               self.layouts.receptive_layout(w).add(w)


    # Event loop
    def event_loop(self):
        """Retrieve and process X events.

        This method implements Minx's event loop. Once we enter this
        function, Minx will initiate an infinite loop, wherein it
................................................................................
        @par
        If you decide not to use wm.start(), look at the Minx code to
        see how and what you will need to do to effectively use this
        function.

        """
        logger.info('entering event loop')
        while not self._quit:
            try:
                logger.debug('waiting for event')
                e = self.display.get_event()
                logger.debug('got event {}'.format(e))
                self.hooks.trigger(str(e), e)

            # Tried to query the window hierarchy of a non-existent
            # window. This happens when a window is destroyed and we
            # receive various other notifications before the destroy
            # notification comes in. Well, there's not much to be done in
            # this situation; so, just log the error and keep going.
            except minxlib.query_tree_error as e:
                logger.warning(e)

            # Tried to focus a non-existent window (happens sometimes
            # upon window destruction, causing the resulting unmap and
            # focus notification handlers to go out-of-sync with the X
            # server's internal state). We can use the protocol error
            # that ensues as an opportunity to sync the internal states
            # of the window manager and X server.
................................................................................
            # stabilized enough for us to be absolutely sure this bit of
            # code is no longer required.
            except minxlib.change_window_attributes as e:
                logger.warning(e)
                if e.error_code == minxlib.bad_window:
                   pass # ignore protocol error (should be okay)

            # Generic protocol error
            except minxlib.protocol_error as e:
                logger.warning(e)

            # Some other exception: log it and keep going
            except:
                logger.warning('received {} exception'.
                               format(sys.exc_info()[0]))
                logger.debug(traceback.format_exc())

        logger.info('exiting event loop')


    # API to focus next window
    def focus_next(self):
        """Focus next window.

        This function passes input focus to the next window in the
        window manager's list of top-level windows that can accept input
................................................................................
        """
        L = self._focus_list
        old_head = L.head()
        L.forward()
        new_head = L.head()
        if new_head != old_head:
           logger.info('switching focus from {} to {}'.
                       format(old_head.id, new_head.id))
           window.unfocus(old_head, self.config)
           window.  focus(new_head, self.config)
        #else: new_head == old_head ==> focus list empty or only one window

    # API to focus previous window
    def focus_prev(self):
        """Focus previous window.

        This function passes input focus to the previous window in the
................................................................................
        """
        L = self._focus_list
        old_head = L.head()
        L.backward()
        new_head = L.head()
        if new_head != old_head:
           logger.info('switching focus from {} to {}'.
                       format(old_head.id, new_head.id))
           window.unfocus(old_head, self.config)
           window.  focus(new_head, self.config)
        #else: new_head == old_head ==> focus list empty or only one window

    # Kill a window
    def kill(self, w = None):
        """Kill window.

        @param w The @ref minxlib::window "minxlib.window" to be killed.

        This function kills the X client application that created the
        window w and all of its other windows and X resources. If w is
        not supplied by the caller, this function will kill the
        currently focused window.

        """
................................................................................
        if w != None: # there is a focused window to kill
           w.kill()

    # Kill a window using brute force
    def nuke(self, w = None):
        """Kill window using brute force.

        @param w The @ref minxlib::window "minxlib.window" to be nuked.

        This function kills the X client application that created the
        window w and all of its other windows and X resources. If w is
        not supplied by the caller, this function will kill the
        currently focused window.

        @note Whereas the kill() function attempts a graceful shutdown,
................................................................................
                           format(sys.exc_info()[0], cmd))
            logger.debug(traceback.format_exc())

    # Add newly created top-level window to focus list
    def add_to_focus_list(self, w):
        """Add a newly created top-level window to the focus list.

        @param w The @ref minxlib::window "minxlib.window" to be added.


        This function is meant to be used internally by Minx.
        Specifically, the wm constructor calls it to take over
        management of any top-level windows that might have been created
        before Minx was started. Also, the <tt>MapNotify</tt> handler in
        the xevents class uses it to add a newly mapped top-level window
        to the focus list and to focus it.
................................................................................

        End-users should not use this function.

        """
        L = self._focus_list
        f = L.head()
        if f != None:
           logger.debug('unfocusing {}'.format(f.id))
           window.unfocus(f, self.config)
        logger.debug('adding {} to focus list and focusing'.format(w.id))

        L.add(w)
        window.focus(w, self.config)

    # Whether or not manage the given window
    def manage(self, w):
        """Check if given window should be managed or not.

        @param  w The @ref minxlib::window "minxlib.window" to be checked.
        @return True if Minx should manage w, false if it should ignore w.
................................................................................
        for r in self.hooks.trigger('manage_hook', w):
            if r == False:  # some manage hook returned False
               return False # therefore, ignore w

        # If we get here, all manage hooks passed w ==> we should
        # manage it...
        return True

    # Quit window manager
    def quit(self):
        """Quit the window manager.

        This function sets a flag that will eventually result in Minx
        exiting its event loop. It is meant to be invoked via a key
        binding or other similar action.

        """
        self._quit = True

#-----------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Changes to core/xevents.py.

36
37
38
39
40
41
42

43
44
45
46
47
48
49
..
92
93
94
95
96
97
98



99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125








126




127
128
129
130

131

132
133
134





135
136
137
138
139
140



141
142


143
144
145
146
147
148
149
150
151
152
153
154
155
156


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

172
173
174
175

#----------------------------- IMPORTS ---------------------------------

# Standard library
import logging

# Minx

import window
import minxlib

#-------------------------- MODULE LOGGER ------------------------------

logger = logging.getLogger(__name__)

................................................................................
        h.add('x_map_notify',        self._on_map)
        h.add('x_unmap_notify',      self._on_unmap)
        h.add('x_key_press',         self._on_key_press)

    # Whether or not window specified by x_create_notify event should
    # be managed by Minx or ignored.
    def _manage(self, e):



        return (not e.override_redirect and

                e.parent in self._wm._root_windows and
                not window.exists(e.target) and
                self._wm.manage(e.target))

    # When a new top-level window is created, the window manager has to
    # update its internal state to include this new window in its list
    # of top-level windows it has to manage.
    #
    # NOTE: We do not add new top-level windows to the focus list right
    # away. Instead, we wait for them to be mapped first before putting
    # them on the focus list. This is because some applications (e.g.,
    # GNOME and XFCE terminal) create hidden top-level windows that
    # should never receive input focus.
    def _on_create(self, e):
        if self._manage (e):
           logger.info('creating top-level window {}'.format(e.target.id))
           window.window(self._wm, e.target, e.x, e.y, e.width, e.height)

    # What to do when a window wants to be configured: pass configure
    # request through as-is.

    def _on_configure_request(self, e):
        logger.info ('configuring window {}'.format(e.target.id))
        logger.debug('configure request details: ' +
                     'geom = {}x{}+{}+{}, bw = {}, mask = {:#010b}'.
                     format(e.width, e.height, e.x, e.y,
                            e.border_width, e.value_mask))








        e.target.configure(e.x, e.y, e.width, e.height, e.border_width,




                           e.above , e.stack_mode, e.value_mask)

    # What to do when a window wants to be shown on-screen: top-level
    # windows are handled by a callback in the minx.core.window module;

    # all other windows are mapped as-is.

    def _on_map_request(self, e):
        if not window.exists(e.target): # not a top-level window
           logger.info('mapping non-top-level window {}'.format(e.target.id))





           e.target.show()

    # What to do when a top-level window is successfully shown
    # on-screen: we should add it to the window manager's focus list and
    # focus it.
    def _on_map(self, e):



        if window.exists(e.target):
           self._wm.add_to_focus_list(window.get(e.target))



    # What to do when a top-level window is taken off-screen: remove it
    # from the window manager's focus list and focus the next window on
    # the focus list.
    def _on_unmap(self, e):
        if window.exists (e.target):
           w = window.get(e.target)
           L = self._wm._focus_list
           logger.info('removing {} from focus list'.format(e.target.id))
           L.remove(w)
           f = L.head()
           if f != None:
              logger.info('focusing {}'.format(f._window.id))
              f.focus()



    # What to do when F1 is pressed: switch focus to next window.
    def _on_key_press(self, e):
        logger.info('keypress: {}, keycode: {}, mask: {:#06x}'.
                    format(e.key, e.keycode, e.mask))
        self._wm.hooks.trigger(e.key)

#-----------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #

# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################







>







 







>
>
>

>
|
<













|
<


|
>

|




>
>
>
>
>
>
>
>
|
>
>
>
>
|


<
>
|
>

<
|
>
>
>
>
>
|





>
>
>
|
|
>
>





|
|
|
|
|
|
|
|
|
>
>

|













>




36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
93
94
95
96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

146
147
148
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204

#----------------------------- IMPORTS ---------------------------------

# Standard library
import logging

# Minx
import layman
import window
import minxlib

#-------------------------- MODULE LOGGER ------------------------------

logger = logging.getLogger(__name__)

................................................................................
        h.add('x_map_notify',        self._on_map)
        h.add('x_unmap_notify',      self._on_unmap)
        h.add('x_key_press',         self._on_key_press)

    # Whether or not window specified by x_create_notify event should
    # be managed by Minx or ignored.
    def _manage(self, e):
        prop = e.target.properties()
        logger.debug('window {} class = {}, name = {}'.
                     format(e.target.id, prop['class'], prop['name']))
        return (not e.override_redirect and
                prop['class'] != 'minx.layout' and
                e.parent in self._wm.root_windows and

                self._wm.manage(e.target))

    # When a new top-level window is created, the window manager has to
    # update its internal state to include this new window in its list
    # of top-level windows it has to manage.
    #
    # NOTE: We do not add new top-level windows to the focus list right
    # away. Instead, we wait for them to be mapped first before putting
    # them on the focus list. This is because some applications (e.g.,
    # GNOME and XFCE terminal) create hidden top-level windows that
    # should never receive input focus.
    def _on_create(self, e):
        if self._manage (e):
           self._wm.layouts.receptive_layout(e.target).add(e.target)


    # What to do when a window wants to be configured: pass configure
    # request through as-is except for top-level windows, which have to
    # be resized according to their managing layout's policy.
    def _on_configure_request(self, e):
        logger.info ('configure request for window {}'.format(e.target.id))
        logger.debug('configure request details: ' +
                     'geom = {}x{}+{}+{}, bw = {}, mask = {:#010b}'.
                     format(e.width, e.height, e.x, e.y,
                            e.border_width, e.value_mask))
        try:
            layout = self._wm.layouts.find(e.parent)
            x, y, w, h = layout.configure_request(e.target, e.x, e.y,
                                                  e.width, e.height)

        except layman.unknown_layout: # pass configure request through as-is
            logger.debug('window {} not a top-level window'.
                         format(e.target.id))
            x, y, w, h = (e.x, e.y, e.width, e.height)

        logger.debug('setting window {} geometry to {}x{}+{}+{}'.
                     format(e.target.id, w, h, x, y))
        e.target.configure(x, y, w, h, e.border_width,
                           e.above, e.stack_mode, e.value_mask)

    # What to do when a window wants to be shown on-screen: top-level

    # windows will be passed to their managing layout for possible
    # reconfiguration before being mapped; all other windows are mapped
    # as-is.
    def _on_map_request(self, e):

        logger.info('map request for window {}'.format(e.target.id))
        try:
            layout = self._wm.layouts.find(e.parent)
            layout.map_request(e.target)
        except layman.unknown_layout:
            pass
        e.target.show()

    # What to do when a top-level window is successfully shown
    # on-screen: we should add it to the window manager's focus list and
    # focus it.
    def _on_map(self, e):
        try:
            self._wm.layouts.find(e.parent)
            logger.debug('adding top-level window {} to focus list'.
                         format(e.target.id))
            self._wm.add_to_focus_list(e.target)
        except layman.unknown_layout: # not a top-level window
            pass

    # What to do when a top-level window is taken off-screen: remove it
    # from the window manager's focus list and focus the next window on
    # the focus list.
    def _on_unmap(self, e):
        try:
            self._wm.layouts.find(e.parent)
            L = self._wm._focus_list
            logger.info('removing {} from focus list'.format(e.target.id))
            L.remove(e.target)
            f = L.head()
            if f != None:
               logger.info('focusing {}'.format(f.id))
               window.focus(f, self._wm.config)
        except layman.unknown_layout: # not a top-level window
            pass

    # What to do when a key is pressed.
    def _on_key_press(self, e):
        logger.info('keypress: {}, keycode: {}, mask: {:#06x}'.
                    format(e.key, e.keycode, e.mask))
        self._wm.hooks.trigger(e.key)

#-----------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Changes to dox/CMakeLists.txt.

27
28
29
30
31
32
33








34
35
36
37
38
39
40
#

# Min cmake version
cmake_minimum_required(VERSION 2.8)

# Project name
project(minxdoc)









# Custom target for API documentation
find_package(Doxygen)
if(DOXYGEN_FOUND)
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/doxyfile.in"
                   "${CMAKE_CURRENT_BINARY_DIR}/doxyfile" @ONLY)
    add_custom_target(doc







>
>
>
>
>
>
>
>







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#

# Min cmake version
cmake_minimum_required(VERSION 2.8)

# Project name
project(minxdoc)

# Check if we have Xinerama and, if so, set a CMake variable that will
# tell Doxygen so that it generates the documentation for the optional
# Xinerama-dependent parts of Minx.
find_package(X11)
if(X11_Xinerama_FOUND)
    set(minxdoc_PREDEFINED MINXLIB_HAS_XINERAMA)
endif()

# Custom target for API documentation
find_package(Doxygen)
if(DOXYGEN_FOUND)
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/doxyfile.in"
                   "${CMAKE_CURRENT_BINARY_DIR}/doxyfile" @ONLY)
    add_custom_target(doc

Changes to dox/doxyfile.in.

670
671
672
673
674
675
676

677
678
679
680
681
682
683
....
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
# The INPUT tag can be used to specify the files and/or directories that contain 
# documented source files. You may enter file names like "myfile.cpp" or 
# directories like "/usr/src/myproject". Separate the files or directories 
# with spaces.

INPUT = @CMAKE_SOURCE_DIR@/minxlib  \
        @CMAKE_SOURCE_DIR@/core     \

        @CMAKE_SOURCE_DIR@/dox

# This tag can be used to specify the character encoding of the source files 
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
# also the default input encoding. Doxygen uses libiconv (or the iconv built 
# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
# the list of possible encodings.
................................................................................
# are defined before the preprocessor is started (similar to the -D option of 
# gcc). The argument of the tag is a list of macros of the form: name 
# or name=definition (no spaces). If the definition and the = are 
# omitted =1 is assumed. To prevent a macro definition from being 
# undefined via #undef or recursively expanded use the := operator 
# instead of the = operator.

PREDEFINED             = 

# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
# this tag can be used to specify a list of macro names that should be expanded. 
# The macro definition that is found in the sources will be used. 
# Use the PREDEFINED tag if you want to use a different macro definition that 
# overrules the definition found in the source code.








>







 







|







670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
....
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
# The INPUT tag can be used to specify the files and/or directories that contain 
# documented source files. You may enter file names like "myfile.cpp" or 
# directories like "/usr/src/myproject". Separate the files or directories 
# with spaces.

INPUT = @CMAKE_SOURCE_DIR@/minxlib  \
        @CMAKE_SOURCE_DIR@/core     \
        @CMAKE_SOURCE_DIR@/layout   \
        @CMAKE_SOURCE_DIR@/dox

# This tag can be used to specify the character encoding of the source files 
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
# also the default input encoding. Doxygen uses libiconv (or the iconv built 
# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
# the list of possible encodings.
................................................................................
# are defined before the preprocessor is started (similar to the -D option of 
# gcc). The argument of the tag is a list of macros of the form: name 
# or name=definition (no spaces). If the definition and the = are 
# omitted =1 is assumed. To prevent a macro definition from being 
# undefined via #undef or recursively expanded use the := operator 
# instead of the = operator.

PREDEFINED             = @minxdoc_PREDEFINED@

# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
# this tag can be used to specify a list of macro names that should be expanded. 
# The macro definition that is found in the sources will be used. 
# Use the PREDEFINED tag if you want to use a different macro definition that 
# overrules the definition found in the source code.

Changes to dox/mainpage.hh.

54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72













73
74
75
76
77
78
79
    @section sec_core Minx Core

    Here are the different classes in Minx's Python core:

        @li @ref minx.core.wm.wm "wm"
        @li @ref minx.core.config.config "config"
        @li @ref grp_minx_core_hooks "hooks"

        @li @ref grp_minx_version "version"
        @li @ref grp_minx_core_window "window"
        @li @ref minx.core.focus_list.focus_list "focus_list"
        @li @ref minx.core.xevents.xevents "xevents"

    Not all of the above classes are meant to be used directly by
    end-users. At this time, in fact, only the @ref minx.core.wm.wm "wm",
    @ref minx.core.config.config "config", and
    @ref grp_minx_core_hooks "hooks" classes are for public
    consumption. The @ref grp_minx_version "minx.version" object may
    also be used. Everything else should be considered internal to Minx.














    <hr>

    @section sec_minxlib minxlib

    As you can tell from the above, Minx is written in Python and is
    largely concerned with the details of laying out windows,
    circulating input focus, and so on. However, in order to be able to







>












>
>
>
>
>
>
>
>
>
>
>
>
>







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
    @section sec_core Minx Core

    Here are the different classes in Minx's Python core:

        @li @ref minx.core.wm.wm "wm"
        @li @ref minx.core.config.config "config"
        @li @ref grp_minx_core_hooks "hooks"
        @li @ref grp_minx_core_layman "layman"
        @li @ref grp_minx_version "version"
        @li @ref grp_minx_core_window "window"
        @li @ref minx.core.focus_list.focus_list "focus_list"
        @li @ref minx.core.xevents.xevents "xevents"

    Not all of the above classes are meant to be used directly by
    end-users. At this time, in fact, only the @ref minx.core.wm.wm "wm",
    @ref minx.core.config.config "config", and
    @ref grp_minx_core_hooks "hooks" classes are for public
    consumption. The @ref grp_minx_version "minx.version" object may
    also be used. Everything else should be considered internal to Minx.

    <hr>

    @section sec_layouts Minx Layouts

    Minx comes with the following layout classes:

        @li @ref minx.layout.base.base "base"
        @li @ref minx.layout.full.full "full"

    You can implement your own layouts by deriving from the
    @ref minx.layout.base.base "minx.layout.base" class and overriding
    some of its methods.

    <hr>

    @section sec_minxlib minxlib

    As you can tell from the above, Minx is written in Python and is
    largely concerned with the details of laying out windows,
    circulating input focus, and so on. However, in order to be able to

Added layout/__init__.py.











































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#
# __init__.py -- initialization for minx.layout module
#

#
# Copyright (C) 2012 The Minx Project Developers
#
# See wiki/copyright.wiki in the top-level directory of the Minx source
# distribution for the full list of authors who have contributed to this
# project.
#

#
# This file is part of Minx.
#
# Minx is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# Minx is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Minx. If not, see <http://www.gnu.org/licenses/>.
#

#------------------------------- IMPORTS --------------------------------

# Pull in various classes from minx.layout so that end-users see them,
# for example, as minx.layout.full instead of minx.layout.full.full, and
# so on.
from base import base
from full import full

#------------------------------- EXPORTS --------------------------------

__all__ = ["base", "full"]

#-----------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Added layout/base.py.















































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
#
# base.py -- base class for Minx layouts
#

#
# Copyright (C) 2012 The Minx Project Developers
#
# See wiki/copyright.wiki in the top-level directory of the Minx source
# distribution for the full list of authors who have contributed to this
# project.
#

#
# This file is part of Minx.
#
# Minx is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# Minx is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Minx. If not, see <http://www.gnu.org/licenses/>.
#

#-------------------------- MODULE DOC STRING ---------------------------

"""@package minx.layout.base
Base class for Minx layouts.

This module implements a base class for layouts.

"""

#------------------------------- IMPORTS --------------------------------

# Standard library
import logging

# Minx
import minx.core.layman
from minx.core import minxlib

#---------------------------- MODULE LOGGER -----------------------------

logger = logging.getLogger(__name__)

#--------------------------- EVENT HANDLERS -----------------------------

# Since layouts use X windows to manage their windows, the layout window
# itself will be visible when a layout has no windows to manage. We don't
# want that; we want layouts to be invisible. To effect that
# invisibility, we map the layout's window when a window is added to the
# layout and unmap it when it no longer has any visible windows.
#
# The following event handlers take care of the above-mentioned strategy
# for keeping layouts invisible.
#
# NOTE: Rather than rely on the event dispatching implemented in xevents,
# we add our own hooks for the relevant X events because we don't want
# layout subclasses to be able to override this mechanism nor require
# them to have to call base class methods in order to ensure correct
# operation of things such as layout invisibility.

# Need the layout manager maintained by the main window manager object so
# that we can find the layouts for different windows.
_layouts = None

# When a window is reparented, show its layout if the layout isn't
# already visible and then let the layout object know so it can readjust
# window geometries as required.
def _on_reparent(e):
    try:
        logger.debug('window {} reparented by {}'.
                     format(e.target.id, e.new_parent.id))

        layout = _layouts.find(e.new_parent)
        logger.debug('window {} being managed by layout {}'.
                     format(e.target.id, layout))

        if not layout.window.is_mapped():
           logger.debug('showing layout {}'.format(layout))
           layout.window.show()

        layout.reparented(e.target)

    except minx.core.layman.unknown_layout:
        logger.warning('no layout corresponding to window {}'.
                       format(e.new_parent))
        logger.debug('window {} not being managed (bogus reparent_notify?)'.
                     format(e.target.id))

# When a window is unmapped, also unmap its layout if the layout has no
# more visible windows.
def _on_unmap(e):
    try:
        logger.debug('window {} unmapped'.format(e.target.id))

        layout = _layouts.find(e.parent)
        logger.debug('window {} being managed by layout {}'.
                     format(e.target.id, layout))

        for w in layout.window.children():
            if w.is_mapped():
               logger.debug('layout {} has visible child {}; no need to hide'.
                            format(layout, w.id))
               return

        logger.debug('hiding layout {} because it has no visible children'.
                     format(layout))
        layout.window.hide()

    except minx.core.layman.unknown_layout:
        pass

# Whenever focus changes, inform the layouts managing the affected
# windows (in case they need to readjust window geometries to take
# un/focused border sizes into account).
def _on_focus_in(e):
    try:
        _layouts.find(e.target.parent()).focused(e.target)
    except minx.core.layman.unknown_layout:
        pass

def _on_focus_out(e):
    try:
        _layouts.find(e.target.parent()).unfocused(e.target)
    except minx.core.layman.unknown_layout:
        pass

#-------------------------------- CLASS ---------------------------------

class base:
    """A base class for Minx layouts.

    This class implements a base class from which all Minx layout classes
    must be derived. This class is not meant to be instantiated; instead,
    you should instantiate one of the layout subclasses provided by Minx
    (or one of the custom layouts you implement yourself).

    """

    # Constructor
    def __init__(self, m, p, r = None):
        """Layout base class constructor.

        @param m The @ref minx.core.wm.wm "main window manager object".
        @param p The layout's parent @ref minxlib::window "minxlib.window".
        @param r The rectangle within parent window assigned to this layout.

        Each layout class's constructor must call this base class
        constructor, passing it at least the parameters m and p, which we
        need because a layout is basically an X window encapsulated by a
        Python object with some additional methods tacked on. Thus, in
        order to be able to create the layout window, we need the display
        connection (which is in the @ref minx.core.wm.wm "wm" class) and
        the parent window of the layout window.

        The parameter r is optional. It specifies the rectangle within
        the parent window that the layout will occupy. It should be a
        tuple of four integers. The first two integers in this tuple
        specify the x and y coordinates respectively of the layout
        window's top-left corner; the third and fourth integers are the
        layout window's width and height respectively.

        If r is not supplied by the subclass constructor, the layout will
        occupy the entire area of the parent window.

        After creating the layout's window, this method sets some
        properties on the newly created window and then maps it on screen.

        @note As mentioned earlier, this class is not meant to be
        instantiated directly. Thus, this constructor must only be called
        by subclasses.

        """
        global _layouts
        if _layouts == None:
           _layouts = m.layouts
           m.hooks.add('x_reparent_notify', _on_reparent, m.hooks.MAX_PRIORITY)
           m.hooks.add(   'x_unmap_notify', _on_unmap,    m.hooks.MAX_PRIORITY)
           m.hooks.add('x_focus_in',  _on_focus_in )
           m.hooks.add('x_focus_out', _on_focus_out)

        x, y, w, h = p.geometry()[:4] # don't need border width
        if r != None: # use supplied rectangle instead of entire parent window
           x, y, w, h = r

        self.window = m.display.create_window(p, x, y, w, h)

        name = 'minx.layout.{}.{}'.format(self.__class__.__name__,
                                          self.window.id)
        self.window.set_properties({'class': 'minx.layout', 'name' : name})
        self.window.select_events(minxlib.substructure_redirect_mask |
                                  minxlib.substructure_notify_mask)

    # Convert layout to string
    def __str__(self):
        """Return layout's name.

        This method will convert the layout object to a human-readable
        string representation (which is useful for debugging as well as
        identifying layouts).

        """
        prop = self.window.properties()
        return prop['name']

    # Check if layout will manage window
    def will_manage(self, w):
        """Check if this layout is willing to manage specified window.

        @param w The @ref minxlib::window "minxlib.window" to be managed.

        This method returns True if the layout is willing to manage the
        window w, False otherwise.

        The intent of this function is to allow layouts to decide whether
        or not they want to accept a window. For example, if a layout is
        written to expressly work with a particular application, it can
        return True for that application's windows and False for all the
        others.

        This method may be overridden by layout subclasses. The base
        class implementation returns True. Thus, unless a subclass
        overrides this function, it will be taken as willing to manage
        all windows.

        """
        logger.debug('layout {} will manage window {}'.format(self, w.id))
        return True

    # Manage specified window
    def add(self, w):
        """Add a window to be managed by this layout.

        @param w The @ref minxlib::window "minxlib.window" to be managed.

        This method tells the layout that it should manage the given
        window, which involves, among other things, reparenting the
        client window so that this layout becomes its parent.

        """
        w.select_events(minxlib.focus_change_mask)
        w.reparent(self.window)

    # Reparent notification
    def reparented(self, w):
        """Reparent notification.

        @param w The @ref minxlib::window "minxlib.window" that was reparented.

        When the @ref minx.core.wm.wm "main window manager object"
        receives a <tt>reparent_notify</tt> message in its event loop, it
        will eventually end up calling this method on the layout object
        that has reparented the window w.

        This method is meant to be overridden by layout subclasses. The
        base class implementation does nothing.

        """
        logger.warning('unhandled reparent_notify by layout ({})'.
                       format(self))

    # Configure requests
    def configure_request(self, n, x, y, w, h):
        """Decide window geometry on configure requests for top-level windows.

        @param  n The @ref minxlib::window "minxlib.window" to be configured.
        @param  x x-coordinate of window's top-left corner.
        @param  y y-coordinate of window's top-left corner.
        @param  w Window width.
        @param  h Window height.
        @return Tuple containing new geometry.

        When the @ref minx.core.wm.wm "main window manager object"
        receives a <tt>configure_request</tt> message in its event loop,
        it will eventually end up calling this method on the layout
        object that has reparented the window n. This method is meant to
        be overridden by layout subclasses; the base class implementation
        does nothing.

        Subclasses should return a 4-tuple of integers containing the new
        window geometry to apply in accordance with their particular
        layout policies. The first two numbers in this tuple specify the
        x and y coordinates of the window's top-left corner; the third
        and fourth numbers are the window's width and height
        respectively.

        The @ref minx.core.wm.wm "wm" object's <tt>configure_request</tt>
        event handler will use the returned tuple to honour the window's
        configure request. To clarify, layouts should not directly add a
        hook for <tt>x_configure_request</tt>; instead, they should let
        the main window manager object do that and override this method
        to let the main <tt>wm</tt> object know how to configure the
        windows they are managing.

        """
        logger.warning('unhandled configure_request by layout ({})'.
                       format(self))
        return (x, y, w, h)

    # Map requests
    def map_request(self, w):
        """Resize top-level window before it gets mapped to screen.

        @param w The @ref minxlib::window "minxlib.window" to be mapped.

        When the @ref minx.core.wm.wm "main window manager object"
        receives a <tt>map_request</tt> message in its event loop, it
        will eventually end up calling this method on the layout object
        that has reparented the window w. This method is meant to be
        overridden by layout subclasses; the base class implementation
        does nothing.

        The intent of this method is to give layouts an opportunity to
        move and resize the windows they are managing just before a
        window gets mapped. Layouts should not themselves map the window
        (though no harm should come from that). Rather, they should let
        the @ref minx.core.wm.wm "main window manager object" map windows
        and, in this method, if necessary, restrict themselves to
        reconfiguring their windows to match their layout policy.

        """
        logger.warning('unhandled map_request by layout ({})'.
                       format(self))

    # Focus change events
    def focused(self, w):
        """Focus-in notification.

        @param w The focused @ref minxlib::window "minxlib.window".

        When the @ref minx.core.wm.wm "main window manager object"
        receives a <tt>focus_in</tt> message in its event loop, it will
        eventually end up calling this method on the layout object that
        has reparented the window w. This method is meant to be
        overridden by layout subclasses; the base class implementation
        does nothing.

        The intent of this method is to inform layouts about changes in
        the input focus. When a window receives the input focus, the
        window manager may change its border color and size. And when
        that happens, the layout managing the newly focused window may
        well have to adjust the geometries of one/more/all of the windows
        it is managing to take into account the new border size.

        """
        logger.warning('unhandled focus_in for window {} by layout {}'.
                       format(w.id, self))

    def unfocused(self, w):
        """Focus-out notification.

        @param w The @ref minxlib::window "minxlib.window" that lost focus.

        When the @ref minx.core.wm.wm "main window manager object"
        receives a <tt>focus_out</tt> message in its event loop, it will
        eventually end up calling this method on the layout object that
        has reparented the window w. This method is meant to be
        overridden by layout subclasses; the base class implementation
        does nothing.

        The intent of this method is to inform layouts about changes in
        the input focus. When a window loses the input focus, the window
        manager may change its border color and size. Of course, when
        that happens, the layout managing the newly unfocused window may
        well have to adjust the geometries of one/more/all of the windows
        it is managing to take into account the new border size.

        """
        logger.warning('unhandled focus_out for window {} by layout {}'.
                       format(w.id, self))

#-----------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Added layout/full.py.

































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#
# full.py -- layout that occupies the entire area of its parent window
#

#
# Copyright (C) 2012 The Minx Project Developers
#
# See wiki/copyright.wiki in the top-level directory of the Minx source
# distribution for the full list of authors who have contributed to this
# project.
#

#
# This file is part of Minx.
#
# Minx is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# Minx is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Minx. If not, see <http://www.gnu.org/licenses/>.
#

#-------------------------- MODULE DOC STRING ---------------------------

"""@package minx.layout.full
Layout that occupies the entire area of its parent window.

This module implements a layout that resizes all the windows it manages
so that they occupy the entire area of the layout's parent window.

"""

#------------------------------- IMPORTS --------------------------------

# Standard library
import logging

# Minx
from base import base

#-------------------------- MODULE LOGGER ------------------------------

logger = logging.getLogger(__name__)

#-------------------------------- CLASS ---------------------------------

class full(base):
    """Layout to occupy full area of parent window.

    This layout resizes all the windows it manages so that they occupy
    the entire area of the layout's parent window. Obviously, this
    strategy means that only one application window will be visible at a
    time.

    """

    # Create layout object
    def __init__(self, m, p, r = None):
        """Create a full layout object.

        @param m The @ref minx.core.wm.wm "main window manager object."
        @param p The layout's parent @ref minxlib::window "minxlib.window".
        @param r The rectangle within parent window assigned to this layout.

        When you create a full layout object, you should supply the main
        window manager object so that the layout can access the X server
        connection, any config settings it needs, etc. Additionally, the
        layout needs to know its parent window.

        The optional parameter r specifies the rectangle within the
        parent window that the layout will occupy. If it is not supplied,
        the layout will occupy the entire area of the parent window. If
        it is given, it should be a tuple containing four integers, the
        first two of which are the x and y coordinates of the layout's
        target area's top-left corner and the remaining two are the
        rectangle's width and height.

        Since Minx layouts are X windows, this constructor will create a
        child window of the specified parent, set appropriate properties
        to mark it as a layout, set the event mask, etc.

        """
        base.__init__(self, m, p, r)

    # Reparent notification
    def reparented(self, w):
        """Reparent notification.

        @param w The @ref minxlib::window "minxlib.window" that was reparented.

        When this layout manages a window, in the resulting reparent
        notification, we ensure that the size of the managed window w
        matches the size of the layout itself.

        """
        logger.info('window {} reparented by full layout ({})'.
                    format(w.id, self.window.id))
        self._resize(w)

    # Configure request
    def configure_request(self, n, x, y, w, h):
        """Configure request handler.

        @param  n The @ref minxlib::window "minxlib.window" to be configured.
        @param  x x-coordinate of window's top-left corner.
        @param  y y-coordinate of window's top-left corner.
        @param  w Window width.
        @param  h Window height.
        @return Tuple containing new geometry.

        This layout resizes the windows it manages to always occupy the
        entire area available to the layout. Thus, in response to a
        configure request for one of its windows, it will return the
        layout's geometry (adjusted to take into account the target
        window's border width).

        """
        logger.debug('full layout {} configuring top-level window {}'.
                     format(self.window.id, n.id))

        b = n.geometry()[4] # window's border width
        d = self.window.geometry()[:4]
        logger.debug('window {} border width = {}, '.format(n.id, b) +
                     'layout geometry = {}x{}+{}+{}'.format(d[2], d[3],
                                                            d[0], d[1]))
        d[2] -= 2*b # reduce window width  to ensure borders are visible
        d[3] -= 2*b # reduce window height to ensure borders are visible
        return (0, 0, d[2], d[3])

    # Map request
    def map_request(self, w):
        """Resize top-level window before it gets mapped to screen.

        @param w The @ref minxlib::window "minxlib.window" to be mapped.

        Before the full layout maps a window, it will compare the
        window's geometry against its own. If the two don't match, it
        will resize the window to make it fill the layout's entire area.

        """
        logger.debug('full layout {} resizing window {} before mapping'.
                     format(self.window.id, w.id))
        self._resize(w)

    # Focus change events
    def focused(self, w):
        """Focus-in notification.

        @param w The focused @ref minxlib::window "minxlib.window".

        When a window receives the input focus, resize it to account for
        potentially new border size.

        """
        logger.debug('full layout {} resizing focused window {}'.
                     format(self.window.id, w.id))
        self._resize(w)

    def unfocused(self, w):
        """Focus-out notification.

        @param w The unfocused @ref minxlib::window "minxlib.window".

        When a window loses the input focus, resize it to account for
        potentially new border size.

        """
        logger.debug('full layout {} resizing unfocused window {}'.
                     format(self.window.id, w.id))
        self._resize(w)

    # Resize window to fill layout
    def _resize(self, w):
        """Resize top-level window to fill entire layout area.

        @param w The @ref minxlib::window "minxlib.window" to be mapped.

        This is a helper method that compares the window w's geometry
        against the full layout's geometry and, if the two don't match,
        resizes w to make it fill the layout's entire area (taking into
        account the border width of w).

        @note This is a private method meant to be used only by the full
        layout class itself and should not be called directly by clients.

        """
        b = w.geometry() # window geometry with border width
        g = b[:4]        # window geometry w/o  border width
        d = self.window.geometry()
        logger.debug('window geometry: {}x{}+{}+{} [{}], '.
                     format(b[2], b[3], b[0], b[1], b[4]) +
                     'full layout {} geometry: {}x{}+{}+{}'.
                     format(self.window.id, d[2], d[3], d[0], d[1]))

        d[2] -= 2*b[4] # reduce window width  to ensure borders are visible
        d[3] -= 2*b[4] # reduce window height to ensure borders are visible
        if g[2:] == d[2:4]: # window and layout have the same size
           pass
        else: # need to adjust window size to match layout
           logger.info('full layout {} resetting window {} '.
                       format(self.window.id, w.id) +
                       'geometry to {}x{}+0+0'.format(d[2], d[3]))
           w.move_resize(0, 0, d[2], d[3])

#-----------------------------------------------------------------------

##############################################
# Editor config:                             #
##############################################
# Local Variables:                           #
# indent-tabs-mode: nil                      #
# py-indent-offset: 4                        #
# python-indent: 4                           #
# End:                                       #
##############################################
# vim: set expandtab shiftwidth=4 tabstop=4: #
##############################################

Changes to minxlib/CMakeLists.txt.

39
40
41
42
43
44
45







46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
find_package(Boost COMPONENTS python REQUIRED)

# Compiler flags
add_definitions(-Wall -Wextra)
include_directories(${X11_INCLUDE_DIR}
                    ${Boost_INCLUDE_DIRS}
                    ${PYTHON_INCLUDE_DIRS})








# Libraries we need to link against
set(minxlib_LIBS ${minxlib_LIBS}
    ${X11_LIBRARIES} ${Boost_LIBRARIES} ${PYTHON_LIBRARIES})

# Sources
set(minxlib_SOURCES python.cc type_info.cc util.cc logging.cc keymap.cc
                    exception.cc display.cc window.cc event.cc)


# Add version.cc to source list if we're on the release branch (version
# numbering API does not exist on development branches).
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version.cc")
    set(minxlib_SOURCES ${minxlib_SOURCES} version.cc)
    add_definitions(-DMINXLIB_HAS_VERSION_API)
endif()







>
>
>
>
>
>
>







|
>







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
find_package(Boost COMPONENTS python REQUIRED)

# Compiler flags
add_definitions(-Wall -Wextra)
include_directories(${X11_INCLUDE_DIR}
                    ${Boost_INCLUDE_DIRS}
                    ${PYTHON_INCLUDE_DIRS})

# Check if we have Xinerama
if(X11_Xinerama_FOUND)
    set(X11_LIBRARIES ${X11_LIBRARIES} ${X11_Xinerama_LIB})
    add_definitions(-DMINXLIB_HAS_XINERAMA)
    set(minxlib_DOXYGEN_DEFINES MINXLIB_HAS_XINERAMA)
endif()

# Libraries we need to link against
set(minxlib_LIBS ${minxlib_LIBS}
    ${X11_LIBRARIES} ${Boost_LIBRARIES} ${PYTHON_LIBRARIES})

# Sources
set(minxlib_SOURCES python.cc type_info.cc util.cc logging.cc keymap.cc
                    exception.cc event.cc display.cc
                    window.cc root_window.cc)

# Add version.cc to source list if we're on the release branch (version
# numbering API does not exist on development branches).
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version.cc")
    set(minxlib_SOURCES ${minxlib_SOURCES} version.cc)
    add_definitions(-DMINXLIB_HAS_VERSION_API)
endif()

Changes to minxlib/display.cc.

28
29
30
31
32
33
34

35
36
37
38





39
40
41
42
43
44
45
...
132
133
134
135
136
137
138











139
140
141
142


143
144



145

146









147
148
149

150
151
152
153
154
155
156
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182










183
184
185
186
187
188
189
...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

253
254

255
256
257
258
259
260
261
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "display.hh"

#include "window.hh"
#include "event.hh"
#include "logging.hh"
#include "python.hh"






// Standard C++
#include <set>

//------------------ INITIALIZATION AND CLEAN-UP -----------------------

namespace minxlib {
................................................................................
    m_display_map[d]->m_fatal = true ;
    fatal_error::raise() ;
    return -1 ;
}

//---------------------------- Xlib API --------------------------------












std::vector<window> display::get_root_windows()
{
    throw_fatal() ;



    int n = ScreenCount(m_display) ;
    logger.debug() << n << " root windows" ;





    std::vector<window> root_windows ;









    root_windows.reserve(n) ;
    for (int i = 0; i < n; ++i)
        root_windows.push_back(window(this, RootWindow(m_display, i))) ;

    return root_windows ;
}

std::vector<window> display::get_top_level_windows()
{
    throw_fatal() ;

................................................................................
            logger.warning() << "unable to query screen " << screen ;
            continue ;
        }
        if (children) {
            for (unsigned int i = 0; i < num_children; ++i) {
                logger.debug() << "screen "   << screen
                               << ", window " << i << " = " << children[i] ;
                windows.push_back(window(this, children[i])) ;
            }
            XFree(children) ;
        }
    }
    logger.info() << "total number of top-level windows = " << windows.size() ;
    return windows ;
}











std::vector<std::string> display::get_keyboard_mapping()
{
    throw_fatal() ;

    int min_keycode, max_keycode ;
    XDisplayKeycodes(m_display, &min_keycode, &max_keycode) ;
................................................................................

boost::shared_ptr<event> display::get_event()
{
    throw_fatal() ;

    XEvent e ;
    XNextEvent(m_display, &e) ;
    return event::create(e, this) ;
}

//------------------------ PYTHON INTERFACE ----------------------------

// Register Boost.Python converter to convert C++ objects to their
// Python equivalents so that things like std::vector become Python
// lists.
static void register_py_converters()
{
    // display::get_root_windows() returns an STL vector of
    // minxlib::window objects that we would like to convert into a
    // Python list.
    typedef std::vector<window> window_list ;
    py::to_python_converter<window_list, universal_converter<window_list> >() ;

    // display::get_keyboard_mapping() returns an STL vector of STL
    // strings that we would like to convert into a Python list of
    // Python strings.
    typedef std::vector<std::string> string_list ;
    py::to_python_converter<string_list, universal_converter<string_list> >() ;
}

// Export display class to Python
void display::pythonize()
{
    register_py_converters() ;

    py::class_<display, boost::noncopyable>("display",
        py::init<std::string, bool>((py::arg("name") = std::string(""),
                                     py::arg("sync") = false ))).

        def("get_root_windows",      &display::get_root_windows).
        def("get_top_level_windows", &display::get_top_level_windows).

        def("get_keyboard_mapping",  &display::get_keyboard_mapping ).
        def("get_event",             &display::get_event) ;

    logger = logging::get_logger("display") ;
}

} // namespace minxlib







>




>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
|



>
>
|
<
>
>
>
|
>
|
>
>
>
>
>
>
>
>
>
|
|
|
>







 







|







>
>
>
>
>
>
>
>
>
>







 







|




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



<
<



>


>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
...
257
258
259
260
261
262
263
264
265
266
267
268


















269
270
271


272
273
274
275
276
277
278
279
280
281
282
283
284
285
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "display.hh"
#include "root_window.hh"
#include "window.hh"
#include "event.hh"
#include "logging.hh"
#include "python.hh"

// Xlib
#ifdef MINXLIB_HAS_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif

// Standard C++
#include <set>

//------------------ INITIALIZATION AND CLEAN-UP -----------------------

namespace minxlib {
................................................................................
    m_display_map[d]->m_fatal = true ;
    fatal_error::raise() ;
    return -1 ;
}

//---------------------------- Xlib API --------------------------------

window
display::
create_window(const window& parent, int x, int y, int width, int height)
{
    throw_fatal() ;

    Window w = XCreateSimpleWindow(m_display, parent,
                                   x, y, width, height, 0, 0, 0) ;
    return window(m_display, w) ;
}

std::vector<root_window> display::get_root_windows()
{
    throw_fatal() ;

    std::vector<root_window> root_windows ;

    int n = 0 ;

#ifdef MINXLIB_HAS_XINERAMA
    XineramaScreenInfo* screens = XineramaQueryScreens(m_display, &n) ;
    if (n > 0)
    {
        logger.info() << "Xinerama enabled; " << n << " screens" ;
        root_windows.reserve(n) ;
        for (int i = 0; i < n; ++i)
            root_windows.push_back(root_window(m_display, i, screens[i])) ;
        XFree(screens) ;
    }
    else
#endif
    {
        n = ScreenCount(m_display) ;
        logger.info() << "no Xinerama; " << n << " independent screens" ;
        root_windows.reserve(n) ;
        for (int i = 0; i < n; ++i)
            root_windows.push_back(root_window(m_display, i)) ;
    }
    return root_windows ;
}

std::vector<window> display::get_top_level_windows()
{
    throw_fatal() ;

................................................................................
            logger.warning() << "unable to query screen " << screen ;
            continue ;
        }
        if (children) {
            for (unsigned int i = 0; i < num_children; ++i) {
                logger.debug() << "screen "   << screen
                               << ", window " << i << " = " << children[i] ;
                windows.push_back(window(m_display, children[i])) ;
            }
            XFree(children) ;
        }
    }
    logger.info() << "total number of top-level windows = " << windows.size() ;
    return windows ;
}

window display::get_focused_window()
{
    throw_fatal() ;

    Window w ; int r ;
    XGetInputFocus(m_display, &w, &r) ;
    logger.debug() << "currently focused window = " << w ;
    return  window(m_display, (w == PointerRoot || w == None) ? 0 : w) ;
}

std::vector<std::string> display::get_keyboard_mapping()
{
    throw_fatal() ;

    int min_keycode, max_keycode ;
    XDisplayKeycodes(m_display, &min_keycode, &max_keycode) ;
................................................................................

boost::shared_ptr<event> display::get_event()
{
    throw_fatal() ;

    XEvent e ;
    XNextEvent(m_display, &e) ;
    return event::create(e, m_display) ;
}

//------------------------ PYTHON INTERFACE ----------------------------



















// Export display class to Python
void display::pythonize()
{


    py::class_<display, boost::noncopyable>("display",
        py::init<std::string, bool>((py::arg("name") = std::string(""),
                                     py::arg("sync") = false ))).
        def("create_window",         &display::create_window   ).
        def("get_root_windows",      &display::get_root_windows).
        def("get_top_level_windows", &display::get_top_level_windows).
        def("get_focused_window",    &display::get_focused_window   ).
        def("get_keyboard_mapping",  &display::get_keyboard_mapping ).
        def("get_event",             &display::get_event) ;

    logger = logging::get_logger("display") ;
}

} // namespace minxlib

Changes to minxlib/display.hh.

74
75
76
77
78
79
80

81
82
83
84
85
86
87
...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
...
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
...
236
237
238
239
240
241
242























243
244
245
246
247
248
249
250
251







252
253
254
255
256
257
258
259
260
...
263
264
265
266
267
268
269













270
271
272
273
274
275
276
//------------------------- CLASS DEFINITION ---------------------------

namespace minxlib {

// Forward declarations
class event ;
class window;


/**
    @brief Encapsulate the details of the connection to the X server.

    This class provides an API for the Python parts of Minx to be able
    to talk to the display server. It wraps around the relevant parts of
    Xlib and exposes its functionality to Python via @boostpylink.
................................................................................
    take care of the details of tracking the multiple connections,
    accessing them from different threads, etc.

    @par
    In short, don't do these funky things; minxlib and the minx library
    are not designed for it and you really shouldn't need such
    functionality anyway (multithreaded window manager with multiple
    connections to the X server? really?  why? don't you have enough
    problems in your life already?).
*/
class display: boost::noncopyable {
    // A display object's basic purpose is to wrap around the Xlib
    // Display structure.
    Display* m_display ;

................................................................................
        clean-up and then terminate the process. The display object will
        become unusable after a fatal_error exception is thrown. Any
        attempt to use it after a fatal error will result in continued
        fatal_error exceptions.
    */
    display(const std::string& name = std::string(), bool sync = false) ;

    /**
        @brief  "Convert" a display object to a Display*.
        @return Display* corresponding to this display object.

        Need this when we have to make Xlib calls from classes such as
        minxlib::window.

        @note This function is only used inside of minxlib, i.e., by the
        C++ side of Minx.

        @par
        We could make this a cast operator. But then we would have to
        pass references to minxlib::display objects to classes such as
        minxlib::window. And that would not really work if we need to
        use the assignment operator on window objects (e.g., storing in
        an std::vector) because C++ doesn't allow references to be
        assigned.

        @par
        Therefore, the only other alternative would be to dereference
        the minxlib::display* object in the minxlib::window methods to
        trigger the automatic cast from minxlib::display* to Xlib's
        Display*. But that is certainly not clearer than calling an
        explicit get method like this one and is no less aesthetic.

        @note Maybe we can avoid this entire hassle by simply passing
        Display* to the minxlib::window constructor? The only trouble
        with that would be in case minxlib::window ends up needing some
        API from this class. For now, leaving this getter in place...
    */
    Display* xlib() {return m_display ;}

    /**
        @brief  Close the connection to the X server.
        @return Nothing.

        When a display object is destroyed, it will automatically sever
        its connection to the display server.
    */
................................................................................

        This function exposes the display class's interface so that it
        can be used by the Python parts of Minx. It is meant to be
        called by the Boost.Python initialization code in python.cc.
    */
    static void pythonize() ;
























    /**
        @brief  Retrieve the list of root windows.
        @return List of root windows.

        This function queries the X server for all its screens and their
        corresponding root windows. The returned list holds the root
        windows in the screen order, i.e., the first element of the list
        is the root window for the first screen, the second is the root
        window for the second screen, so on and so forth.







    */
    std::vector<window> get_root_windows() ;

    /**
        @brief  Retrieve top-level windows across all screens.
        @return List of top-level windows.

        This function queries the X server for all the top-level windows
        on all screens. Do not rely on the returned list being in any
................................................................................
        This function is meant to be used by
        @ref minx.core.wm.wm "minx.core.wm" just before it enters its
        event loop so that Minx can manage any extant top-level windows
        that were created before Minx was started.
    */
    std::vector<window> get_top_level_windows() ;














    /**
        @brief  Retrieve currently supported keysyms in string form.
        @return List of strings containing current keysyms.

        The X server assigns unique integer codes to each physical key
        on the keyboard. However, since different keyboard models and
        brands can have different keycodes, applications usually deal







>







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









>
>
>
>
>
>
>

|







 







>
>
>
>
>
>
>
>
>
>
>
>
>







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
186
187
188
189
190
191
192
































193
194
195
196
197
198
199
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
//------------------------- CLASS DEFINITION ---------------------------

namespace minxlib {

// Forward declarations
class event ;
class window;
class root_window ;

/**
    @brief Encapsulate the details of the connection to the X server.

    This class provides an API for the Python parts of Minx to be able
    to talk to the display server. It wraps around the relevant parts of
    Xlib and exposes its functionality to Python via @boostpylink.
................................................................................
    take care of the details of tracking the multiple connections,
    accessing them from different threads, etc.

    @par
    In short, don't do these funky things; minxlib and the minx library
    are not designed for it and you really shouldn't need such
    functionality anyway (multithreaded window manager with multiple
    connections to the X server? really? why? don't you have enough
    problems in your life already?).
*/
class display: boost::noncopyable {
    // A display object's basic purpose is to wrap around the Xlib
    // Display structure.
    Display* m_display ;

................................................................................
        clean-up and then terminate the process. The display object will
        become unusable after a fatal_error exception is thrown. Any
        attempt to use it after a fatal error will result in continued
        fatal_error exceptions.
    */
    display(const std::string& name = std::string(), bool sync = false) ;

































    /**
        @brief  Close the connection to the X server.
        @return Nothing.

        When a display object is destroyed, it will automatically sever
        its connection to the display server.
    */
................................................................................

        This function exposes the display class's interface so that it
        can be used by the Python parts of Minx. It is meant to be
        called by the Boost.Python initialization code in python.cc.
    */
    static void pythonize() ;

    /**
        @brief  Create a window.
        @param  p The new window's parent window.
        @param  x The x-coordinate of the new window's origin.
        @param  y The y-coordinate of the new window's origin.
        @param  w The new window's width.
        @param  h The new window's height.
        @return The newly created window.

        This function creates a new window by calling
        XCreateSimpleWindow(), wraps the result in a minxlib::window
        object and returns that.

        The parameter p <b>must</b> be an existing window; x and y are
        relative to the parent's origin.

        @note Although this function can churn out arbitrary windows
        parented by arbitrary other windows, end-user code should most
        definitely not abuse it in that manner. It is meant to be used
        only to create layouts.
    */
    window create_window(const window& p, int x, int y, int w, int h) ;

    /**
        @brief  Retrieve the list of root windows.
        @return List of root windows.

        This function queries the X server for all its screens and their
        corresponding root windows. The returned list holds the root
        windows in the screen order, i.e., the first element of the list
        is the root window for the first screen, the second is the root
        window for the second screen, so on and so forth.

        @note On multi-head setups with Xinerama, there is only one
        screen, and, thus, only one root window as far as the X server is
        concerned. Nonetheless, this function will still return as many
        root windows as there are physical monitors; each of these root
        windows will have the same window ID, but their geometries will
        be different.
    */
    std::vector<root_window> get_root_windows() ;

    /**
        @brief  Retrieve top-level windows across all screens.
        @return List of top-level windows.

        This function queries the X server for all the top-level windows
        on all screens. Do not rely on the returned list being in any
................................................................................
        This function is meant to be used by
        @ref minx.core.wm.wm "minx.core.wm" just before it enters its
        event loop so that Minx can manage any extant top-level windows
        that were created before Minx was started.
    */
    std::vector<window> get_top_level_windows() ;

    /**
        @brief  Get the window with the input focus.
        @return Currently focused window.

        This function uses XGetInputFocus() to determine which X window
        currently has the input focus and returns that wrapped in a
        @ref minxlib::window "minxlib::window" object.

        If no window has the input focus, this function will return a
        window with the ID zero.
    */
    window get_focused_window() ;

    /**
        @brief  Retrieve currently supported keysyms in string form.
        @return List of strings containing current keysyms.

        The X server assigns unique integer codes to each physical key
        on the keyboard. However, since different keyboard models and
        brands can have different keycodes, applications usually deal

Changes to minxlib/event.cc.

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
352
353
354
355
356
357
358




359
360
361
362
363
364




365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
...
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
...
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "event.hh"
#include "display.hh"
#include "logging.hh"
#include "python.hh"
#include "keymap.hh"

// Xlib
#include <X11/XKBlib.h>

// Boost
#include <boost/foreach.hpp>

// Standard C++

#include <ostream>
#include <stdexcept>

//------------------------ EVENTS BASE CLASS ---------------------------

namespace minxlib {

static logging logger ;

// Public API for creating event objects
boost::shared_ptr<event> event::create(const XEvent& e, display* d)
{
    typedef factory<event, int, cr_func, registry> event_factory ;
    try
    {
        boost::shared_ptr<event> p(event_factory::create(e.type, e, d)) ;
        return p ;
    }
................................................................................
                         << " on window " << e.xany.window ;
        boost::shared_ptr<event> p(new event(e, d, e.xany.window)) ;
        return p ;
    }
}

// Protected constructor
event::event(const XEvent& e, display* d, Window w)
    : type(e.type),
      serial(e.xany.serial),
      send_event(e.xany.send_event == True),
      target(d, w)
{}

// Virtual destructor
................................................................................
// subclasses with the event factory and defining a stream output
// operator so Boost.Python can generate __str__.
#define register_minxlib_event(xlib_code, subclass)                     \
    std::ostream& operator<<(std::ostream& os, const subclass&) {       \
        return os << "x_" #subclass ;                                   \
    }                                                                   \
    bool subclass::registered = event::registry::add(xlib_code,         \
             create_object<event, subclass, const XEvent&, display*>,   \
             subclass::pythonize)

//---------------- SUBSTRUCTURE REDIRECTION EVENTS ---------------------

// Map request events
register_minxlib_event(MapRequest, map_request) ;

map_request::map_request(const XEvent& e, display* d)
    : event (e, d, e.xmaprequest.window),
      parent(d, e.xmaprequest.parent)
{}

void map_request::pythonize()
{
    py::class_<map_request, py::bases<event> >("map_request", py::no_init).
................................................................................
        def_readonly("parent", &map_request::parent).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Configure request events
register_minxlib_event(ConfigureRequest, configure_request) ;

configure_request::configure_request(const XEvent& e, display* d)
    : event (e, d, e.xconfigurerequest.window),
      parent(d, e.xconfigurerequest.parent),
      x(e.xconfigurerequest.x), y(e.xconfigurerequest.y),
      width(e.xconfigurerequest.width), height(e.xconfigurerequest.height),
      border_width(e.xconfigurerequest.border_width),
      above(d, e.xconfigurerequest.above),
      stack_mode(e.xconfigurerequest.detail),
................................................................................
        def_readonly("value_mask",   &configure_request::value_mask  ).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Circulate request events
register_minxlib_event(CirculateRequest, circulate_request) ;

circulate_request::circulate_request(const XEvent& e, display* d)
    : event (e, d, e.xcirculaterequest.window),
      parent(d, e.xcirculaterequest.parent),
      place(e.xcirculaterequest.place)
{}

void circulate_request::pythonize()
{
................................................................................
}

//------------- STRUCTURE AND SUBSTRUCTURE NOTIFICATIONS ---------------

// Window creation
register_minxlib_event(CreateNotify, create_notify) ;

create_notify::create_notify(const XEvent& e, display* d)
    : event (e, d, e.xcreatewindow.window),
      parent(d, e.xcreatewindow.parent),
      x(e.xcreatewindow.x), y(e.xcreatewindow.y),
      width(e.xcreatewindow.width), height(e.xcreatewindow.height),
      border_width(e.xcreatewindow.border_width),
      override_redirect(e.xcreatewindow.override_redirect == True)
{}
................................................................................
        def_readonly("override_redirect", &create_notify::override_redirect  ).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window reparenting
register_minxlib_event(ReparentNotify, reparent_notify) ;

reparent_notify::reparent_notify(const XEvent& e, display* d)
    : event (e, d, e.xreparent.window),
      parent(d, e.xreparent.event ),
      new_parent(d, e.xreparent.parent),
      x(e.xreparent.x), y(e.xreparent.y),
      override_redirect(e.xreparent.override_redirect == True)
{}

................................................................................
        def_readonly("override_redirect", &reparent_notify::override_redirect).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window configuration
register_minxlib_event(ConfigureNotify, configure_notify) ;

configure_notify::configure_notify(const XEvent& e, display* d)
    : event (e, d, e.xconfigure.window),
      parent(d, e.xconfigure.event ),
      x(e.xconfigure.x), y(e.xconfigure.y),
      width(e.xconfigure.width), height(e.xconfigure.height),
      border_width(e.xconfigure.border_width),
      above(d, e.xconfigure.above),
      override_redirect(e.xconfigure.override_redirect == True)
................................................................................
        def_readonly("override_redirect",&configure_notify::override_redirect).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Gravity notifications
register_minxlib_event(GravityNotify, gravity_notify) ;

gravity_notify::gravity_notify(const XEvent& e, display* d)
    : event (e, d, e.xgravity.window),
      parent(d, e.xgravity.event ),
      x(e.xgravity.x), y(e.xgravity.y)
{}

void gravity_notify::pythonize()
{
................................................................................
        def_readonly("y",      &gravity_notify::y     ).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window mapping
register_minxlib_event(MapNotify, map_notify) ;

map_notify::map_notify(const XEvent& e, display* d)
    : event (e, d, e.xmap.window),
      parent(d, e.xmap.event ),
      override_redirect(e.xmap.override_redirect == True)
{}

void map_notify::pythonize()
{
................................................................................
        def_readonly("override_redirect", &map_notify::override_redirect).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window unmapping
register_minxlib_event(UnmapNotify, unmap_notify) ;

unmap_notify::unmap_notify(const XEvent& e, display* d)
    : event (e, d, e.xunmap.window),
      parent(d, e.xunmap.event ),
      from_configure(e.xunmap.from_configure == True)
{}

void unmap_notify::pythonize()
{
................................................................................
        def_readonly("from_configure", &unmap_notify::from_configure).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Circulate notifications
register_minxlib_event(CirculateNotify, circulate_notify) ;

circulate_notify::circulate_notify(const XEvent& e, display* d)
    : event (e, d, e.xcirculate.window),
      parent(d, e.xcirculate.event ),
      place (e.xcirculate.place)
{}

void circulate_notify::pythonize()
{
................................................................................
        def_readonly("place",  &circulate_notify::place ).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window destruction
register_minxlib_event(DestroyNotify, destroy_notify) ;

destroy_notify::destroy_notify(const XEvent& e, display* d)
    : event (e, d, e.xdestroywindow.window),
      parent(d, e.xdestroywindow.event )
{}

void destroy_notify::pythonize()
{
    py::class_<destroy_notify, py::bases<event> >("destroy_notify",
................................................................................

// If a given keycode and modifier mask correspond to a key binding,
// we'll return the hook name supplied by user for the passive grab for
// that keystroke. Otherwise, we'll simply translate the keycode to a
// string (via translation to a keysym).
static std::string keycode_to_string(Display* d, KeyCode k, unsigned int m)
{




    try
    {
        return keymap::get(k, m) ; // user-defined name for a key binding
    }
    catch (std::out_of_range&) // no key binding
    {




        return XKeysymToString(XkbKeycodeToKeysym(d, k, 0, 0)) ;
    }
}

// Common attributes of keyboard events
key_event_details::key_event_details(const XKeyEvent& e, display* d)
    : root(d, e.root), child(d, e.subwindow),
      time(e.time),
      x(e.x), y(e.y), x_root(e.x_root), y_root(e.y_root),
      mask(e.state), keycode(e.keycode),
      key(keycode_to_string(d->xlib(), e.keycode, e.state)),
      same_screen(e.same_screen == True)
{}

// Key presses
register_minxlib_event(KeyPress, key_press) ;

key_press::key_press(const XEvent& e, display* d)
    : event(e, d, e.xkey.window),
      key_event_details(e.xkey, d)
{}

void key_press::pythonize()
{
    py::class_<key_press, py::bases<event> >("key_press", py::no_init).
................................................................................
        def_readonly("same_screen", &key_press::same_screen).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Key releases
register_minxlib_event(KeyRelease, key_release) ;

key_release::key_release(const XEvent& e, display* d)
    : event(e, d, e.xkey.window),
      key_event_details(e.xkey, d)
{}

void key_release::pythonize()
{
    py::class_<key_release, py::bases<event> >("key_release", py::no_init).
................................................................................
focus_change_details::focus_change_details(const XFocusChangeEvent& e)
    : mode(e.mode), detail(e.detail)
{}

// FocusIn
register_minxlib_event(FocusIn, focus_in) ;

focus_in::focus_in(const XEvent& e, display* d)
    : event(e, d, e.xfocus.window),
      focus_change_details(e.xfocus)
{}

void focus_in::pythonize()
{
    py::class_<focus_in, py::bases<event> >("focus_in", py::no_init).
................................................................................
        def_readonly("detail", &focus_in::detail).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// FocusOut
register_minxlib_event(FocusOut, focus_out) ;

focus_out::focus_out(const XEvent& e, display* d)
    : event(e, d, e.xfocus.window),
      focus_change_details(e.xfocus)
{}

void focus_out::pythonize()
{
    py::class_<focus_out, py::bases<event> >("focus_out", py::no_init).







<











>










|







 







|







 







|







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







|







 







>
>
>
>






>
>
>
>





|




|






|







 







|







 







|







 







|







28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
...
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
...
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "event.hh"

#include "logging.hh"
#include "python.hh"
#include "keymap.hh"

// Xlib
#include <X11/XKBlib.h>

// Boost
#include <boost/foreach.hpp>

// Standard C++
#include <iomanip>
#include <ostream>
#include <stdexcept>

//------------------------ EVENTS BASE CLASS ---------------------------

namespace minxlib {

static logging logger ;

// Public API for creating event objects
boost::shared_ptr<event> event::create(const XEvent& e, Display* d)
{
    typedef factory<event, int, cr_func, registry> event_factory ;
    try
    {
        boost::shared_ptr<event> p(event_factory::create(e.type, e, d)) ;
        return p ;
    }
................................................................................
                         << " on window " << e.xany.window ;
        boost::shared_ptr<event> p(new event(e, d, e.xany.window)) ;
        return p ;
    }
}

// Protected constructor
event::event(const XEvent& e, Display* d, Window w)
    : type(e.type),
      serial(e.xany.serial),
      send_event(e.xany.send_event == True),
      target(d, w)
{}

// Virtual destructor
................................................................................
// subclasses with the event factory and defining a stream output
// operator so Boost.Python can generate __str__.
#define register_minxlib_event(xlib_code, subclass)                     \
    std::ostream& operator<<(std::ostream& os, const subclass&) {       \
        return os << "x_" #subclass ;                                   \
    }                                                                   \
    bool subclass::registered = event::registry::add(xlib_code,         \
             create_object<event, subclass, const XEvent&, Display*>,   \
             subclass::pythonize)

//---------------- SUBSTRUCTURE REDIRECTION EVENTS ---------------------

// Map request events
register_minxlib_event(MapRequest, map_request) ;

map_request::map_request(const XEvent& e, Display* d)
    : event (e, d, e.xmaprequest.window),
      parent(d, e.xmaprequest.parent)
{}

void map_request::pythonize()
{
    py::class_<map_request, py::bases<event> >("map_request", py::no_init).
................................................................................
        def_readonly("parent", &map_request::parent).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Configure request events
register_minxlib_event(ConfigureRequest, configure_request) ;

configure_request::configure_request(const XEvent& e, Display* d)
    : event (e, d, e.xconfigurerequest.window),
      parent(d, e.xconfigurerequest.parent),
      x(e.xconfigurerequest.x), y(e.xconfigurerequest.y),
      width(e.xconfigurerequest.width), height(e.xconfigurerequest.height),
      border_width(e.xconfigurerequest.border_width),
      above(d, e.xconfigurerequest.above),
      stack_mode(e.xconfigurerequest.detail),
................................................................................
        def_readonly("value_mask",   &configure_request::value_mask  ).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Circulate request events
register_minxlib_event(CirculateRequest, circulate_request) ;

circulate_request::circulate_request(const XEvent& e, Display* d)
    : event (e, d, e.xcirculaterequest.window),
      parent(d, e.xcirculaterequest.parent),
      place(e.xcirculaterequest.place)
{}

void circulate_request::pythonize()
{
................................................................................
}

//------------- STRUCTURE AND SUBSTRUCTURE NOTIFICATIONS ---------------

// Window creation
register_minxlib_event(CreateNotify, create_notify) ;

create_notify::create_notify(const XEvent& e, Display* d)
    : event (e, d, e.xcreatewindow.window),
      parent(d, e.xcreatewindow.parent),
      x(e.xcreatewindow.x), y(e.xcreatewindow.y),
      width(e.xcreatewindow.width), height(e.xcreatewindow.height),
      border_width(e.xcreatewindow.border_width),
      override_redirect(e.xcreatewindow.override_redirect == True)
{}
................................................................................
        def_readonly("override_redirect", &create_notify::override_redirect  ).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window reparenting
register_minxlib_event(ReparentNotify, reparent_notify) ;

reparent_notify::reparent_notify(const XEvent& e, Display* d)
    : event (e, d, e.xreparent.window),
      parent(d, e.xreparent.event ),
      new_parent(d, e.xreparent.parent),
      x(e.xreparent.x), y(e.xreparent.y),
      override_redirect(e.xreparent.override_redirect == True)
{}

................................................................................
        def_readonly("override_redirect", &reparent_notify::override_redirect).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window configuration
register_minxlib_event(ConfigureNotify, configure_notify) ;

configure_notify::configure_notify(const XEvent& e, Display* d)
    : event (e, d, e.xconfigure.window),
      parent(d, e.xconfigure.event ),
      x(e.xconfigure.x), y(e.xconfigure.y),
      width(e.xconfigure.width), height(e.xconfigure.height),
      border_width(e.xconfigure.border_width),
      above(d, e.xconfigure.above),
      override_redirect(e.xconfigure.override_redirect == True)
................................................................................
        def_readonly("override_redirect",&configure_notify::override_redirect).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Gravity notifications
register_minxlib_event(GravityNotify, gravity_notify) ;

gravity_notify::gravity_notify(const XEvent& e, Display* d)
    : event (e, d, e.xgravity.window),
      parent(d, e.xgravity.event ),
      x(e.xgravity.x), y(e.xgravity.y)
{}

void gravity_notify::pythonize()
{
................................................................................
        def_readonly("y",      &gravity_notify::y     ).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window mapping
register_minxlib_event(MapNotify, map_notify) ;

map_notify::map_notify(const XEvent& e, Display* d)
    : event (e, d, e.xmap.window),
      parent(d, e.xmap.event ),
      override_redirect(e.xmap.override_redirect == True)
{}

void map_notify::pythonize()
{
................................................................................
        def_readonly("override_redirect", &map_notify::override_redirect).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window unmapping
register_minxlib_event(UnmapNotify, unmap_notify) ;

unmap_notify::unmap_notify(const XEvent& e, Display* d)
    : event (e, d, e.xunmap.window),
      parent(d, e.xunmap.event ),
      from_configure(e.xunmap.from_configure == True)
{}

void unmap_notify::pythonize()
{
................................................................................
        def_readonly("from_configure", &unmap_notify::from_configure).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Circulate notifications
register_minxlib_event(CirculateNotify, circulate_notify) ;

circulate_notify::circulate_notify(const XEvent& e, Display* d)
    : event (e, d, e.xcirculate.window),
      parent(d, e.xcirculate.event ),
      place (e.xcirculate.place)
{}

void circulate_notify::pythonize()
{
................................................................................
        def_readonly("place",  &circulate_notify::place ).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Window destruction
register_minxlib_event(DestroyNotify, destroy_notify) ;

destroy_notify::destroy_notify(const XEvent& e, Display* d)
    : event (e, d, e.xdestroywindow.window),
      parent(d, e.xdestroywindow.event )
{}

void destroy_notify::pythonize()
{
    py::class_<destroy_notify, py::bases<event> >("destroy_notify",
................................................................................

// If a given keycode and modifier mask correspond to a key binding,
// we'll return the hook name supplied by user for the passive grab for
// that keystroke. Otherwise, we'll simply translate the keycode to a
// string (via translation to a keysym).
static std::string keycode_to_string(Display* d, KeyCode k, unsigned int m)
{
    using std::dec ; using std::hex ; using std::setw ; using std::setfill ;
    logger.debug() << "key event: key code = " << dec << static_cast<int>(k)
                   << ", modifier mask = 0x"
                   << hex << setw(4) << setfill('0') << m ;
    try
    {
        return keymap::get(k, m) ; // user-defined name for a key binding
    }
    catch (std::out_of_range&) // no key binding
    {
        logger.debug() << "no key binding for [0x"
                       << hex << setw(4) << setfill('0') << m << " + 0x"
                       << hex << setw(4) << setfill('0')
                       << static_cast<int>(k) << ']' ;
        return XKeysymToString(XkbKeycodeToKeysym(d, k, 0, 0)) ;
    }
}

// Common attributes of keyboard events
key_event_details::key_event_details(const XKeyEvent& e, Display* d)
    : root(d, e.root), child(d, e.subwindow),
      time(e.time),
      x(e.x), y(e.y), x_root(e.x_root), y_root(e.y_root),
      mask(e.state), keycode(e.keycode),
      key(keycode_to_string(d, e.keycode, e.state)),
      same_screen(e.same_screen == True)
{}

// Key presses
register_minxlib_event(KeyPress, key_press) ;

key_press::key_press(const XEvent& e, Display* d)
    : event(e, d, e.xkey.window),
      key_event_details(e.xkey, d)
{}

void key_press::pythonize()
{
    py::class_<key_press, py::bases<event> >("key_press", py::no_init).
................................................................................
        def_readonly("same_screen", &key_press::same_screen).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// Key releases
register_minxlib_event(KeyRelease, key_release) ;

key_release::key_release(const XEvent& e, Display* d)
    : event(e, d, e.xkey.window),
      key_event_details(e.xkey, d)
{}

void key_release::pythonize()
{
    py::class_<key_release, py::bases<event> >("key_release", py::no_init).
................................................................................
focus_change_details::focus_change_details(const XFocusChangeEvent& e)
    : mode(e.mode), detail(e.detail)
{}

// FocusIn
register_minxlib_event(FocusIn, focus_in) ;

focus_in::focus_in(const XEvent& e, Display* d)
    : event(e, d, e.xfocus.window),
      focus_change_details(e.xfocus)
{}

void focus_in::pythonize()
{
    py::class_<focus_in, py::bases<event> >("focus_in", py::no_init).
................................................................................
        def_readonly("detail", &focus_in::detail).
        def(py::self_ns::str(py::self_ns::self)) ; // __str__
}

// FocusOut
register_minxlib_event(FocusOut, focus_out) ;

focus_out::focus_out(const XEvent& e, Display* d)
    : event(e, d, e.xfocus.window),
      focus_change_details(e.xfocus)
{}

void focus_out::pythonize()
{
    py::class_<focus_out, py::bases<event> >("focus_out", py::no_init).

Changes to minxlib/event.hh.

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
...
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
...
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
...
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
// Standard C++
#include <string>

//--------------------- BASE CLASS FOR X EVENTS ------------------------

namespace minxlib {

// Forward declaration
class display ;

/**
    @ingroup grp_minxlib_events
    @brief A generic X event, i.e., XAnyEvent.

    This is a base class for the @ref grp_minxlib_events "event hierarchy"
    exposed by minxlib to the Python parts (i.e., the rest) of Minx. The
    "main" window manager object, viz., @ref minx.core.wm.wm
................................................................................
        In this base class, we don't know exactly which Xlib event
        structure to use from the XEvent union; only the subclasses that
        wrap around specific event types have that information.
        Therefore, we require all subclasses to specify the correct ID
        to use for the target window and ignore whatever is in
        XEvent::xany.
    */
    event(const XEvent& e, display* d, Window w) ;

    /**
        @brief Signature of factory functions for creating events.

        Each event subclass is instantiated by a factory function that
        takes an XEvent structure and a minxlib::display pointer and
        returns a subclass instance upcast to minxlib::event. This type
        provides a convenient name for factory functions matching the
        above-mentioned signature.

        All minxlib::event subclasses are required to implement a
        factory function matching this signature and to register it with
        the base class's
        @ref minxlib::event::registry "registry of subclass object factories".
        Usually, the factory function will simply be the appropriate
        create_object function from minxlib's
        @ref grp_factory "factory infrastructure".

    */
    typedef event* (*cr_func)(const XEvent&, display*) ;

    /**
        @brief Signature of Pythonize functions.

        In addition to a factory function, each subclass of
        minxlib::event has to define a Pythonize function that will have
        the appropriate @boostpylink incantations in order to export its
        interface to minxlib's Python module.
        
        All subclass Pythonize functions will be called by
        event::pythonize(), which is invoked by the main Python
        initialization module python.cc. Pythonize functions take no
        parameters and return nothing.

        Like the factory functions, subclasses must register their
        Pythonize functions with the
................................................................................
        @brief Registry of subclass object factories and Pythonize functions.

        This type defines the event object factory's registry, which
        maps the Xlib event type codes to the corresponding event
        subclass's factory function. Additionally, we store the
        Pythonize functions in the registry as well, which obviates the
        need for another map to hold them.
       
        For the @ref grp_factory "factory pattern" to work, event
        subclasses must define a static boolean data member and, in its
        initializer, call event::registry::add(), passing it the
        appropriate Xlib event code, the subclass's factory function,
        and its Pythonize function.
    */
    typedef factory_map<int, cr_func, py_func> registry ;
................................................................................
        @boostpylink has all sorts of magic in it to convert C++
        objects to their Python equivalents and vice versa. Since
        instances of minxlib::event are created using an object factory,
        i.e., on the heap and accessed via pointers, we need to wrap
        event objects in a boost::shared_ptr to make the Boost.Python
        conversion magic work with minimal effort.
    */
    static boost::shared_ptr<event> create(const XEvent& e, display* d) ;

    /**
        @brief  Export event class to minxlib Python module.
        @return Nothing.

        This function exposes the event class's interface so that it
        can be used by the Python parts of Minx. It is meant to be
................................................................................

    /**
        @brief  Event object clean-up.
        @return Nothing.

        Because minxlib::event is a base class for other event types, it
        is a good idea for it to have a virtual destructor.  This is
        especially necessary for the event class hierarchy becasue all
        events are created using a polymorphic factory that upcasts all
        subclass instances to minxlib::event*. Consequently, we have to
        provide a virtual destructor to ensure subclass objects are
        properly cleaned up when the base class pointers are deleted.

        Furthermore, to get the magic conversions in @boostpylink to
        work properly, we need at least one virtual method in the
................................................................................
// member, and a friend declaration for the appropriate create_event
// factory function.
//
// Instead of typing out all of that over and over, we use the following
// macro to do it for us.
#define MINXLIB_EVENT_SUBCLASS_BOILERPLATE(subclass_name)                   \
    private:                                                                \
        subclass_name(const XEvent&, display*) ;                            \
        static void pythonize();                                            \
        static bool registered ;                                            \
        friend event* create_object<event, subclass_name,                   \
                                    const XEvent&, display*>(const XEvent&, \
                                                             display*)

//---------------- SUBSTRUCTURE REDIRECTION EVENTS ---------------------

/**
    @ingroup grp_minxlib_events
    @brief Encapsulation of MapRequest events.

................................................................................
    honour these configuration requests. These redirections are
    delivered via ConfigureRequest events, which are wrapped by minxlib
    and delivered to Minx's Python core via instances of this class.
*/
struct configure_request: public event {
    /**
        @brief The parent window of the window to be mapped.
        
        Since window managers are usually only concerned with top-level
        windows, we check this field to determine how to handle the
        configure request. If it is equal to the root window of some
        screen, the target window is a top-level window that may need to
        be handled in a special way (e.g., subject to the current layout
        policy).  Non top-level windows will usually have their
        configure requests honoured "as-is."
................................................................................

    const int x ;            ///< Origin x-coordinate relative to parent.
    const int y ;            ///< Origin y-coordinate relative to parent.
    const int width ;        ///< Window width.
    const int height;        ///< Window height.
    const int border_width ; ///< Size of window border.

    const window above  ; ///< Sibling for stacking (N/A to Minx).
    const int stack_mode; ///< Above, Below, TopIf, BottomIf, Opposite (N/A).

    /**
        @brief Bit mask indicating what has to be configured.

        This field specifies which of the above fields are relevant to
        the configure request. Consult Xlib documentation for more
        information.
................................................................................
} ;

/**
    @ingroup grp_minxlib_events
    @brief Encapsulation of ReparentNotify events.

    When a window is reparented (typically by window managers), the X
    server notifies interested clients using these events. Since Minx is
    not a reparenting window manager, these events are not used.
    Nonetheless, minxlib provides this encapsulation.
*/
struct reparent_notify: public event {
    // Additional attributes of a reparent notify event
    const window parent ;          ///< Target window's old or new parent.
    const window new_parent ;      ///< Target window's new parent.
    const int x ;                  ///< Window origin x-coordinate.
    const int y ;                  ///< Window origin y-coordinate.
................................................................................
        @param  e The XEvent structure detailing the keyboard event.
        @param  d The X server connection associated with the event.
        @return A properly constructed key_event_details object.

        A protected constructor because only key_press and key_release
        objects should construct instances of this class.
    */
    key_event_details(const XKeyEvent& e, display* d) ;
} ;

/**
    @ingroup grp_minxlib_events
    @brief Encapsulation of KeyPress events.
*/
struct key_press: public event, public key_event_details {







<
<
<







 







|





|
|
|











|








|







 







|







 







|







 







|







 







|



|
|







 







|







 







|
|







 







|
<
<







 







|







51
52
53
54
55
56
57



58
59
60
61
62
63
64
...
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
...
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
...
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
...
470
471
472
473
474
475
476
477


478
479
480
481
482
483
484
...
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
// Standard C++
#include <string>

//--------------------- BASE CLASS FOR X EVENTS ------------------------

namespace minxlib {




/**
    @ingroup grp_minxlib_events
    @brief A generic X event, i.e., XAnyEvent.

    This is a base class for the @ref grp_minxlib_events "event hierarchy"
    exposed by minxlib to the Python parts (i.e., the rest) of Minx. The
    "main" window manager object, viz., @ref minx.core.wm.wm
................................................................................
        In this base class, we don't know exactly which Xlib event
        structure to use from the XEvent union; only the subclasses that
        wrap around specific event types have that information.
        Therefore, we require all subclasses to specify the correct ID
        to use for the target window and ignore whatever is in
        XEvent::xany.
    */
    event(const XEvent& e, Display* d, Window w) ;

    /**
        @brief Signature of factory functions for creating events.

        Each event subclass is instantiated by a factory function that
        takes an XEvent structure and a display pointer and returns a
        subclass instance upcast to minxlib::event. This type provides a
        convenient name for factory functions matching the
        above-mentioned signature.

        All minxlib::event subclasses are required to implement a
        factory function matching this signature and to register it with
        the base class's
        @ref minxlib::event::registry "registry of subclass object factories".
        Usually, the factory function will simply be the appropriate
        create_object function from minxlib's
        @ref grp_factory "factory infrastructure".

    */
    typedef event* (*cr_func)(const XEvent&, Display*) ;

    /**
        @brief Signature of Pythonize functions.

        In addition to a factory function, each subclass of
        minxlib::event has to define a Pythonize function that will have
        the appropriate @boostpylink incantations in order to export its
        interface to minxlib's Python module.

        All subclass Pythonize functions will be called by
        event::pythonize(), which is invoked by the main Python
        initialization module python.cc. Pythonize functions take no
        parameters and return nothing.

        Like the factory functions, subclasses must register their
        Pythonize functions with the
................................................................................
        @brief Registry of subclass object factories and Pythonize functions.

        This type defines the event object factory's registry, which
        maps the Xlib event type codes to the corresponding event
        subclass's factory function. Additionally, we store the
        Pythonize functions in the registry as well, which obviates the
        need for another map to hold them.

        For the @ref grp_factory "factory pattern" to work, event
        subclasses must define a static boolean data member and, in its
        initializer, call event::registry::add(), passing it the
        appropriate Xlib event code, the subclass's factory function,
        and its Pythonize function.
    */
    typedef factory_map<int, cr_func, py_func> registry ;
................................................................................
        @boostpylink has all sorts of magic in it to convert C++
        objects to their Python equivalents and vice versa. Since
        instances of minxlib::event are created using an object factory,
        i.e., on the heap and accessed via pointers, we need to wrap
        event objects in a boost::shared_ptr to make the Boost.Python
        conversion magic work with minimal effort.
    */
    static boost::shared_ptr<event> create(const XEvent& e, Display* d) ;

    /**
        @brief  Export event class to minxlib Python module.
        @return Nothing.

        This function exposes the event class's interface so that it
        can be used by the Python parts of Minx. It is meant to be
................................................................................

    /**
        @brief  Event object clean-up.
        @return Nothing.

        Because minxlib::event is a base class for other event types, it
        is a good idea for it to have a virtual destructor.  This is
        especially necessary for the event class hierarchy because all
        events are created using a polymorphic factory that upcasts all
        subclass instances to minxlib::event*. Consequently, we have to
        provide a virtual destructor to ensure subclass objects are
        properly cleaned up when the base class pointers are deleted.

        Furthermore, to get the magic conversions in @boostpylink to
        work properly, we need at least one virtual method in the
................................................................................
// member, and a friend declaration for the appropriate create_event
// factory function.
//
// Instead of typing out all of that over and over, we use the following
// macro to do it for us.
#define MINXLIB_EVENT_SUBCLASS_BOILERPLATE(subclass_name)                   \
    private:                                                                \
        subclass_name(const XEvent&, Display*) ;                            \
        static void pythonize();                                            \
        static bool registered ;                                            \
        friend event* create_object<event, subclass_name,                   \
                                    const XEvent&, Display*>(const XEvent&, \
                                                             Display*)

//---------------- SUBSTRUCTURE REDIRECTION EVENTS ---------------------

/**
    @ingroup grp_minxlib_events
    @brief Encapsulation of MapRequest events.

................................................................................
    honour these configuration requests. These redirections are
    delivered via ConfigureRequest events, which are wrapped by minxlib
    and delivered to Minx's Python core via instances of this class.
*/
struct configure_request: public event {
    /**
        @brief The parent window of the window to be mapped.

        Since window managers are usually only concerned with top-level
        windows, we check this field to determine how to handle the
        configure request. If it is equal to the root window of some
        screen, the target window is a top-level window that may need to
        be handled in a special way (e.g., subject to the current layout
        policy).  Non top-level windows will usually have their
        configure requests honoured "as-is."
................................................................................

    const int x ;            ///< Origin x-coordinate relative to parent.
    const int y ;            ///< Origin y-coordinate relative to parent.
    const int width ;        ///< Window width.
    const int height;        ///< Window height.
    const int border_width ; ///< Size of window border.

    const window above  ; ///< Sibling for stacking.
    const int stack_mode; ///< Above, Below, TopIf, BottomIf, Opposite.

    /**
        @brief Bit mask indicating what has to be configured.

        This field specifies which of the above fields are relevant to
        the configure request. Consult Xlib documentation for more
        information.
................................................................................
} ;

/**
    @ingroup grp_minxlib_events
    @brief Encapsulation of ReparentNotify events.

    When a window is reparented (typically by window managers), the X
    server notifies interested clients using these events.


*/
struct reparent_notify: public event {
    // Additional attributes of a reparent notify event
    const window parent ;          ///< Target window's old or new parent.
    const window new_parent ;      ///< Target window's new parent.
    const int x ;                  ///< Window origin x-coordinate.
    const int y ;                  ///< Window origin y-coordinate.
................................................................................
        @param  e The XEvent structure detailing the keyboard event.
        @param  d The X server connection associated with the event.
        @return A properly constructed key_event_details object.

        A protected constructor because only key_press and key_release
        objects should construct instances of this class.
    */
    key_event_details(const XKeyEvent& e, Display* d) ;
} ;

/**
    @ingroup grp_minxlib_events
    @brief Encapsulation of KeyPress events.
*/
struct key_press: public event, public key_event_details {

Changes to minxlib/exception.cc.

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
311
312
313
314
315
316
317

318
319
320
321
322
323
324
...
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402

403
404
405
406
407
408
409
410
411
412
413
414
415

416
417
418
419
420
421
422
423
}

// For all subclasses: retrieve the Python exception class corresponding
// to your type.
PyObject* exception::get_py_exception(type_info t)
{
    py_exception_map::const_iterator it = m_py_exception_map.find(t) ;
    if (it == m_py_exception_map.end()) // unregistered subclass! BUG!
        return m_py_exception_map[typeid(exception)] ;
    return it->second ;
}

// Expose C++ exception class to Python
void exception::pythonize()
{
................................................................................

// Expose protocol_error and its subclasses to Python
void protocol_error::pythonize()
{
    const char* py_name = "protocol_error" ;

    // Create Boost.Python wrapper for protocol_error class

    py::class_<protocol_error, py::bases<exception>,
               boost::shared_ptr<protocol_error> >(py_name, py::no_init).
        def_readonly("serial",       &protocol_error::serial      ).
        def_readonly("error_code",   &protocol_error::error_code  ).
        def_readonly("request_code", &protocol_error::request_code).
        def_readonly("minor_code",   &protocol_error::minor_code  ).
        def_readonly("resource_id",  &protocol_error::resource_id ).
................................................................................
//
// exception.hh uses a macro for the above boilerplate. We do the same
// here because definitions of the above functions and data member all
// do pretty much the exact same thing except that the class names are
// different. This avoids unnecessary repetition and is, therefore, less
// error-prone. Defining a new protocol_error subclass is a simple
// matter of using the macros in the header file and here.
//
// NOTE: The macro that exception.hh uses has the same name as the one
// we use here. However, since exception.hh undefines that macro after
// it is done with it, we can reuse that name here without any trouble.
#define DEFINE_PROTOCOL_ERROR_SUBCLASS(subclass, request_code)              \
                                                                            \
    subclass::subclass(const XErrorEvent* e): protocol_error(e) {}          \
                                                                            \
    void subclass::pythonize()                                              \
    {                                                                       \
        const char* py_name = #subclass ;                                   \
        PyObject*   py_base = get_py_exception(typeid(protocol_error)) ;    \
                                                                            \

        py::class_<subclass, py::bases<protocol_error> >(py_name,           \
                                                         py::no_init).      \
            def(py::self_ns::str(py::self_ns::self)) ;                      \
        set_py_exception(typeid(subclass),                                  \
                         create_py_exception(py_name, py_base)) ;           \
    }                                                                       \
                                                                            \
    bool subclass::registered = protocol_error::registry::                  \
            add(request_code,                                               \
                create_object<protocol_error, subclass, const XErrorEvent*>,\
                subclass::pythonize)

// Various subclasses of protocol_error

DEFINE_PROTOCOL_ERROR_SUBCLASS(set_focus_error, X_SetInputFocus) ;
DEFINE_PROTOCOL_ERROR_SUBCLASS(change_window_attributes,
                               X_ChangeWindowAttributes) ;

// Get rid of boilerplate macro
#undef DEFINE_PROTOCOL_ERROR_SUBCLASS

} // namespace minxlib







|







 







>







 







<
<
<
<









>













>
|







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
...
384
385
386
387
388
389
390




391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
}

// For all subclasses: retrieve the Python exception class corresponding
// to your type.
PyObject* exception::get_py_exception(type_info t)
{
    py_exception_map::const_iterator it = m_py_exception_map.find(t) ;
    if ( it == m_py_exception_map.end()) // unregistered subclass! BUG!
        return m_py_exception_map[typeid(exception)] ;
    return it->second ;
}

// Expose C++ exception class to Python
void exception::pythonize()
{
................................................................................

// Expose protocol_error and its subclasses to Python
void protocol_error::pythonize()
{
    const char* py_name = "protocol_error" ;

    // Create Boost.Python wrapper for protocol_error class
    register_translator<protocol_error>() ;
    py::class_<protocol_error, py::bases<exception>,
               boost::shared_ptr<protocol_error> >(py_name, py::no_init).
        def_readonly("serial",       &protocol_error::serial      ).
        def_readonly("error_code",   &protocol_error::error_code  ).
        def_readonly("request_code", &protocol_error::request_code).
        def_readonly("minor_code",   &protocol_error::minor_code  ).
        def_readonly("resource_id",  &protocol_error::resource_id ).
................................................................................
//
// exception.hh uses a macro for the above boilerplate. We do the same
// here because definitions of the above functions and data member all
// do pretty much the exact same thing except that the class names are
// different. This avoids unnecessary repetition and is, therefore, less
// error-prone. Defining a new protocol_error subclass is a simple
// matter of using the macros in the header file and here.




#define DEFINE_PROTOCOL_ERROR_SUBCLASS(subclass, request_code)              \
                                                                            \
    subclass::subclass(const XErrorEvent* e): protocol_error(e) {}          \
                                                                            \
    void subclass::pythonize()                                              \
    {                                                                       \
        const char* py_name = #subclass ;                                   \
        PyObject*   py_base = get_py_exception(typeid(protocol_error)) ;    \
                                                                            \
        register_translator<subclass>() ;                                   \
        py::class_<subclass, py::bases<protocol_error> >(py_name,           \
                                                         py::no_init).      \
            def(py::self_ns::str(py::self_ns::self)) ;                      \
        set_py_exception(typeid(subclass),                                  \
                         create_py_exception(py_name, py_base)) ;           \
    }                                                                       \
                                                                            \
    bool subclass::registered = protocol_error::registry::                  \
            add(request_code,                                               \
                create_object<protocol_error, subclass, const XErrorEvent*>,\
                subclass::pythonize)

// Various subclasses of protocol_error
DEFINE_PROTOCOL_ERROR_SUBCLASS(query_tree_error, X_QueryTree) ;
DEFINE_PROTOCOL_ERROR_SUBCLASS( set_focus_error, X_SetInputFocus) ;
DEFINE_PROTOCOL_ERROR_SUBCLASS(change_window_attributes,
                               X_ChangeWindowAttributes) ;

// Get rid of boilerplate macro
#undef DEFINE_PROTOCOL_ERROR_SUBCLASS

} // namespace minxlib

Changes to minxlib/exception.hh.

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
...
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
...
716
717
718
719
720
721
722
723

724
725
726
727
728
729
730
        translator for their type. It simply generates a translator for
        type T (which is the exception::translate<T>() function) and calls
        the appropriate @boostpylink API to register
        exception::translate<T>() as the exception translator for type T.
    */
    template<typename T>
    static void register_translator() {
        boost::python::register_exception_translator<T>(translate<T>) ;
    }

public:
    /**
        @brief  Expose exception interface to Python.
        @return Nothing.

................................................................................
        @brief Registry of factory functions for instantiating subclasses.

        This type defines the protocol_error object factory's registry,
        which maps the X protocol error codes to the corresponding
        protocol_error subclass's factory function. Additionally, we
        store the Pythonize functions in the registry as well, which
        obviates the need for another map to hold them.
       
        For the @ref grp_factory "factory pattern" to work,
        protocol_error subclasses must define a static boolean data
        member and, in its initializer, call
        protocol_error::registry::add(), passing it the appropriate X
        protocol error code, the subclass's factory function, and its
        Pythonize function.
    */
................................................................................
        static void pythonize();                                        \
        static bool registered ;                                        \
        friend protocol_error* create_object<protocol_error, subclass,  \
                   const XErrorEvent*>(const XErrorEvent*) ;            \
    }

// Input focus related errors
DECLARE_PROTOCOL_ERROR_SUBCLASS(set_focus_error) ;

DECLARE_PROTOCOL_ERROR_SUBCLASS(change_window_attributes) ;

// Get rid of the boilerplate macro to ensure it can't be used outside
// this file.
#undef DECLARE_PROTOCOL_ERROR_SUBCLASS

} // namespace minxlib







|







 







|







 







|
>







252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
...
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
...
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
        translator for their type. It simply generates a translator for
        type T (which is the exception::translate<T>() function) and calls
        the appropriate @boostpylink API to register
        exception::translate<T>() as the exception translator for type T.
    */
    template<typename T>
    static void register_translator() {
        py::register_exception_translator<T>(translate<T>) ;
    }

public:
    /**
        @brief  Expose exception interface to Python.
        @return Nothing.

................................................................................
        @brief Registry of factory functions for instantiating subclasses.

        This type defines the protocol_error object factory's registry,
        which maps the X protocol error codes to the corresponding
        protocol_error subclass's factory function. Additionally, we
        store the Pythonize functions in the registry as well, which
        obviates the need for another map to hold them.

        For the @ref grp_factory "factory pattern" to work,
        protocol_error subclasses must define a static boolean data
        member and, in its initializer, call
        protocol_error::registry::add(), passing it the appropriate X
        protocol error code, the subclass's factory function, and its
        Pythonize function.
    */
................................................................................
        static void pythonize();                                        \
        static bool registered ;                                        \
        friend protocol_error* create_object<protocol_error, subclass,  \
                   const XErrorEvent*>(const XErrorEvent*) ;            \
    }

// Input focus related errors
DECLARE_PROTOCOL_ERROR_SUBCLASS(query_tree_error) ;
DECLARE_PROTOCOL_ERROR_SUBCLASS( set_focus_error) ;
DECLARE_PROTOCOL_ERROR_SUBCLASS(change_window_attributes) ;

// Get rid of the boilerplate macro to ensure it can't be used outside
// this file.
#undef DECLARE_PROTOCOL_ERROR_SUBCLASS

} // namespace minxlib

Changes to minxlib/factory.hh.

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
        iterator(map_iterator& i): iterator::iterator_adaptor_(i){}

        // Move to next element in sequence
        void increment() {++this->base_reference() ;}

        // Retrieve data currently being "pointed" at
        D& dereference() const {
            map_iterator it = this->base_reference() ;
            return (D&)(it->second.second) ;
            // DEVNOTE: Without the above C-style cast to D&, compiler
            // complains. It also doesn't like static_cast or
            // const_cast. Don't know what's up with that; also, don't
            // care enough to figure it out. The above C-ness works; so,
            // whatever, move on.
        }

        // We want only the factory_map (outer) class to be able to
        // instantiate this iterator.
        friend class factory_map ;

        // Standard Boost.Iterator idiom







|
<
<
<
<
<
<







332
333
334
335
336
337
338
339






340
341
342
343
344
345
346
        iterator(map_iterator& i): iterator::iterator_adaptor_(i){}

        // Move to next element in sequence
        void increment() {++this->base_reference() ;}

        // Retrieve data currently being "pointed" at
        D& dereference() const {
            return const_cast<D&>(this->base_reference()->second.second) ;






        }

        // We want only the factory_map (outer) class to be able to
        // instantiate this iterator.
        friend class factory_map ;

        // Standard Boost.Iterator idiom

Changes to minxlib/keymap.cc.

28
29
30
31
32
33
34

35
36
37
38
39
40

41
42
43
44
45
46



47
48
49
50
51
52
53
..
65
66
67
68
69
70
71

72





73
74
75
76
77






78
79
80
81
82
83
84
85
..
95
96
97
98
99
100
101





102
103
104
105
106
107
108
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "keymap.hh"

#include "util.hh"

// Xlib
#include <X11/keysym.h>

// Standard C++

#include <map>

//-------------------------- IMPLEMENTATION ----------------------------

namespace {




// Store names of user-defined key bindings in this map, which is
// indexed by combining modmask + keycode keystroke combinations.
std::map<uint32_t, std::string> kmap ;

// Which modifiers to consider, which ones to ignore...
uint32_t modmask = 0x00FF ; // default: consider all modifiers

................................................................................

namespace minxlib {
namespace keymap  {

// Insert key binding name s into the keymap
void bind(KeyCode k, unsigned int m, const std::string& s)
{

    kmap[make_key(k, m)] = s ;





}

// Retrieve key binding name from keymap
std::string get(KeyCode k, unsigned int m)
{






    return kmap.at(make_key(k, m)) ; // throws std::out_of_range
}

// Set modmask so as to ignore any Lock modifiers
uint32_t ignore_lock_modifiers(XModifierKeymap* mod_kmap, Display* d)
{
    const KeySym locks[] = {
    #ifdef XK_MISCELLANY
................................................................................

    uint32_t mask = 0U ;
    for(int i = 0; i < n; ++i)
        mask |= minxlib::modmask(locks[i], mod_kmap, d) ;
    ::modmask = (~mask & 0xFF) ;
    return ::modmask ;
}






} // namespace keymap
} // namespace minxlib

//----------------------------------------------------------------------

/**********************************************/







>






>






>
>
>







 







>
|
>
>
>
>
>





>
>
>
>
>
>
|







 







>
>
>
>
>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "keymap.hh"
#include "logging.hh"
#include "util.hh"

// Xlib
#include <X11/keysym.h>

// Standard C++
#include <iomanip>
#include <map>

//-------------------------- IMPLEMENTATION ----------------------------

namespace {

// Module logger
minxlib::logging logger ;

// Store names of user-defined key bindings in this map, which is
// indexed by combining modmask + keycode keystroke combinations.
std::map<uint32_t, std::string> kmap ;

// Which modifiers to consider, which ones to ignore...
uint32_t modmask = 0x00FF ; // default: consider all modifiers

................................................................................

namespace minxlib {
namespace keymap  {

// Insert key binding name s into the keymap
void bind(KeyCode k, unsigned int m, const std::string& s)
{
    using std::hex ; using std::setw ; using std::setfill ;
    uint32_t i = make_key(k, m) ;
    logger.debug() << "inserting keybinding " << s << " [0x"
                   << hex << setw(4) << setfill('0') << m << " + 0x"
                   << hex << setw(4) << setfill('0') << static_cast<int>(k)
                   << "] at index 0x"<< setfill('0') << setw(8) << hex << i ;
    kmap[i] = s ;
}

// Retrieve key binding name from keymap
std::string get(KeyCode k, unsigned int m)
{
    using std::hex ; using std::setw ; using std::setfill ;
    uint32_t i = make_key(k, m) ;
    logger.debug() << "looking for keybinding for [0x"
                   << hex << setw(4) << setfill('0') << m << " + 0x"
                   << hex << setw(4) << setfill('0') << static_cast<int>(k)
                   << "] at index 0x"<< setfill('0') << setw(8) << hex << i ;
    return kmap.at(i) ; // throws std::out_of_range
}

// Set modmask so as to ignore any Lock modifiers
uint32_t ignore_lock_modifiers(XModifierKeymap* mod_kmap, Display* d)
{
    const KeySym locks[] = {
    #ifdef XK_MISCELLANY
................................................................................

    uint32_t mask = 0U ;
    for(int i = 0; i < n; ++i)
        mask |= minxlib::modmask(locks[i], mod_kmap, d) ;
    ::modmask = (~mask & 0xFF) ;
    return ::modmask ;
}

void pythonize()
{
    logger = logging::get_logger("keymap") ;
}

} // namespace keymap
} // namespace minxlib

//----------------------------------------------------------------------

/**********************************************/

Changes to minxlib/keymap.hh.

156
157
158
159
160
161
162













163
164
165
166
167
168
169
    debugging. That is, it doesn't convey anything particularly useful
    to its caller; what we want is to see it in Minx's log so we can
    double-check using the xmodmap program that the key binding mapping
    will work correctly.
*/
uint32_t ignore_lock_modifiers(XModifierKeymap* m, Display* d) ;














} // namespace keymap
} // namespace minxlib

//----------------------------------------------------------------------

#endif // #ifndef MINXLIB_KEYMAP_DOT_HH








>
>
>
>
>
>
>
>
>
>
>
>
>







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
    debugging. That is, it doesn't convey anything particularly useful
    to its caller; what we want is to see it in Minx's log so we can
    double-check using the xmodmap program that the key binding mapping
    will work correctly.
*/
uint32_t ignore_lock_modifiers(XModifierKeymap* m, Display* d) ;

/**
    @ingroup grp_minxlib_keymap
    @brief  Setup keymap module's logger.
    @return Nothing.

    The keymap module does not really have anything exposed to Minx's
    Python core via @boostpylink. However, we do use the Python-based
    logging infrastructure. That's why we need this "pythonization"
    function, which is meant to be called by the Boost.Python
    initialization code in python.cc.
*/
void pythonize() ;

} // namespace keymap
} // namespace minxlib

//----------------------------------------------------------------------

#endif // #ifndef MINXLIB_KEYMAP_DOT_HH

Changes to minxlib/logging.hh.

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
    /**
        @brief  Create a logger object for the specified class.
        @param  name minxlib class for which we want a logger.
        @return A logger object for the specified class.

        This function returns a logger object that can be used by the
        named class to send log messages to the Minx log. It is meant to
        be called by the Pythonize functions of the minxlib::display,
        minxlib::window, minxlib::event, and minxlib::exception classes.
        The <em>.cc</em> files for these classes should define a static
        global <tt>logger</tt> object that is initialized by calling
        this function in the Pythonize function of the above-mentioned
        classes.

        See the sample code in the class description for intended usage
        pattern.
    */
    static logging get_logger(const std::string& name) ;








|
|
|
|
|







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
    /**
        @brief  Create a logger object for the specified class.
        @param  name minxlib class for which we want a logger.
        @return A logger object for the specified class.

        This function returns a logger object that can be used by the
        named class to send log messages to the Minx log. It is meant to
        be called by the Pythonize functions of the various minxlib
        modules that send log messages to the Minx log. The <em>.cc</em>
        files for these modules should define a static global
        <tt>logger</tt> object that is initialized by calling this
        function in the Pythonize function of the above-mentioned
        classes.

        See the sample code in the class description for intended usage
        pattern.
    */
    static logging get_logger(const std::string& name) ;

Changes to minxlib/python.cc.

44
45
46
47
48
49
50

51
52
53

54
55
56
57
58
59



























60
61
62
63
64
65
66
67
68
69



70

71

72
73
74
75
76
77
78
79
80
81
82
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "display.hh"

#include "window.hh"
#include "event.hh"
#include "exception.hh"

#ifdef MINXLIB_HAS_VERSION_API
#include "version.hh"
#endif
#include "python.hh"

//------------------------ PYTHON INTERFACE ----------------------------




























// Standard Boost.Python idiom for exporting C++ classes and functions
// as part of a Python module. In this case, we expose the relevant
// parts of minxlib as a Python module named "minxlib".
//
// DEVNOTE: The order in which we call the initialization functions
// matters! Since the minxlib::exception class acts as a base for other
// exception classes in minxlib, we must setup its Python wrapper first.
BOOST_PYTHON_MODULE(minxlib)
{



    minxlib::exception::pythonize() ;

    minxlib::display  ::pythonize() ;

    minxlib::window   ::pythonize() ;
    minxlib::event    ::pythonize() ;
#ifdef MINXLIB_HAS_VERSION_API
    minxlib::version  ::pythonize() ;
#endif
}

//----------------------------------------------------------------------

/**********************************************/
/* Editor config:                             */







>



>






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






|
|


>
>
>
|
>
|
>
|
|

|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "display.hh"
#include "root_window.hh"
#include "window.hh"
#include "event.hh"
#include "exception.hh"
#include "keymap.hh"
#ifdef MINXLIB_HAS_VERSION_API
#include "version.hh"
#endif
#include "python.hh"

//------------------------ PYTHON INTERFACE ----------------------------

// Register Boost.Python converters to convert C++ objects to their
// Python equivalents and vice versa (so that things like std::vector
// become Python lists and vice versa).
static void register_converters()
{
    using namespace minxlib ;

    typedef std::vector<int> int_list ;
    py::to_python_converter<int_list, universal_converter<int_list> >() ;

    typedef std::vector<std::string> string_list ;
    py::to_python_converter<string_list, universal_converter<string_list> >() ;

    typedef std::vector<window> window_list ;
    py::to_python_converter<window_list,
                            universal_converter<window_list> >() ;

    typedef std::vector<root_window> root_window_list ;
    py::to_python_converter<root_window_list,
                            universal_converter<root_window_list> >() ;

    typedef std::map<std::string, std::string> window_properties ;
    py::to_python_converter<window_properties,
                            universal_converter<window_properties> >() ;
    iterable_converter().from_python<window_properties>() ;
}

// Standard Boost.Python idiom for exporting C++ classes and functions
// as part of a Python module. In this case, we expose the relevant
// parts of minxlib as a Python module named "minxlib".
//
// DEVNOTE: The order in which we call the initialization functions
// matters! For example, since root_window is derived from window, we
// must setup the window class's Python wrapper before root_window.
BOOST_PYTHON_MODULE(minxlib)
{
    register_converters() ;

    using namespace minxlib ;
    exception  ::pythonize();
    display    ::pythonize();
    window     ::pythonize();
    root_window::pythonize();
    event      ::pythonize();
    keymap     ::pythonize();
#ifdef MINXLIB_HAS_VERSION_API
    version    ::pythonize();
#endif
}

//----------------------------------------------------------------------

/**********************************************/
/* Editor config:                             */

Changes to minxlib/python.hh.

60
61
62
63
64
65
66

67
68
69
70

71
72
73
74
75

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
...
157
158
159
160
161
162
163


































































































































































































































164
165
166
167
168
169
170
171
*/
#ifdef __GNUC__
#pragma GCC system_header
#endif

// Now include the Boost header that causes the pain...
#include <boost/python.hpp>


// Other Boost headers
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>


// Standard C++
#include <algorithm>
#include <map>
#include <vector>


//--------------------------- NAMESPACE --------------------------------

namespace minxlib {

// This namespace alias is used throughout minxlib. So, instead of
// repeating it in each .cc file, just put it here.
namespace py = boost::python ;

//--------------------------- CONVERTERS -------------------------------

/*
   Converters to turn C++ objects into their Python equivalents. More
   info at:

   General/Tutorial Explanation of Boost.Python Converters:
       http://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/
................................................................................
template<typename T>
struct universal_converter {
    static PyObject* convert(const T& t) {
        return py::incref(to_python(t).ptr()) ;
    }
} ;



































































































































































































































//----------------------------------------------------------------------

} // namespace minxlib

#endif // #ifndef MINXLIB_PYTHON_DOT_HH

/**********************************************/
/* Editor config:                             */







>




>





>

|







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
*/
#ifdef __GNUC__
#pragma GCC system_header
#endif

// Now include the Boost header that causes the pain...
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>

// Other Boost headers
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/iterator_adaptors.hpp>

// Standard C++
#include <algorithm>
#include <map>
#include <vector>
#include <iterator>

//----------------------------- NAMESPACE -------------------------------

namespace minxlib {

// This namespace alias is used throughout minxlib. So, instead of
// repeating it in each .cc file, just put it here.
namespace py = boost::python ;

//----------------------------- TO PYTHON -------------------------------

/*
   Converters to turn C++ objects into their Python equivalents. More
   info at:

   General/Tutorial Explanation of Boost.Python Converters:
       http://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/
................................................................................
template<typename T>
struct universal_converter {
    static PyObject* convert(const T& t) {
        return py::incref(to_python(t).ptr()) ;
    }
} ;

//---------------------------- FROM PYTHON ------------------------------

/*
    @brief A key-value iterator for converting Python dicts to STL maps.

    Boost.Python's stl_input_iterator iterates over a Python container by
    using its __iter__ method. Unfortunately, for dicts, __iter__
    iterates only over the keys. Thus, trying to in-place construct an
    STL map (see the construct() function in the iterable_converter
    defined later in this file) doesn't work because an STL map stores
    key-value pairs. Consequently, when the iterable_converter
    dereferences a dict's iterator, we need to return an STL pair
    containing both the key and the corresponding value that can be
    inserted into the STL map being built from the Python dict.

    This class implements an adaptor for Boost.Python's
    stl_input_iterator to achieve the dereferencing effect described
    above.

    The type M is expected to be an std::map and it should be the same as
    the type C passed to iterable_converter::construct().
*/
template<typename M>
class stl_map_iterator: public boost::iterator_adaptor<
    stl_map_iterator<M>,       // standard boost::iterator_adaptor idiom
    py::stl_input_iterator<typename M::value_type::first_type>, // base iter.
    typename M::value_type,    // should be std::pair<key, value>
    std::forward_iterator_tag, // iterator category
    typename M::value_type>    // ref. type (make new pair, not ret. ref.)
{
    // When it is derefenced, the base iterator type, viz., the
    // Boost.Python stl_input_iterator that this class adapts, will
    // return a key belonging to its underlying Python dict. In order to
    // be able to convert that to a key-value pair, we need the
    // underlying Python container so we can look up the values
    // corresponding to keys.
    py::object m_dict ;

    // STL map's key and value types (they're basically std::pair objects)
    typedef typename M::value_type:: first_type key_type ;
    typedef typename M::value_type::second_type val_type ;

    // Handy shortcut for referring to the base iterator type that this
    // class is adapting.
    typedef py::stl_input_iterator<key_type> base_type ;

public:
    /**
        @brief  Iterator to the beginning of the underlying container.
        @param  d The Python dict we want to convert to an STL map.
        @param  i The Boost.Python stl_input_iterator for the STL map.
        @return Iterator to beginning of STL map being constructed from Py dict.

        This constructor uses the Boost.Python stl_input_iterator and its
        underlying Python dict to make a suitable iterator for the
        corresponding C++ map that we will build with the
        iterable_converter defined later in this file.
    */
    stl_map_iterator(const py::object& d, base_type& i) ;

    /**
        @brief  Iterator to end of STL map being built from Python dict.
        @return Iterator to end of STL map.

        This constructor builds the iterator returned by the end()
        function in C++ container classes, i.e., it is the iterator to
        one past the end of the map that will be made by the
        iterable_converter class from a Python dict.
    */
    stl_map_iterator() ;

private:
    // Move to next element in sequence (standard Boost iterator adaptor idiom)
    void increment() {++this->base_reference() ;}

    /**
        @brief  Return key-value pair for STL map from Python dict's key.
        @return Key-value pair corresponding to a key.

        As mentioned above, Boost.Python's stl_input_iterator, when
        applied to a dict, will return only a key. However, in the
        corresponding STL map, we want to insert a key-value pair. This
        function is where this map iterator works its magic...

        It first dereferences the base iterator (i.e., the
        stl_input_iterator it is adapting) to get the key from the Python
        dict. After that, it looks up that key in the Python dict to get
        the value corresponding to the key. Finally, it returns an STL
        pair combining the key and value.
    */
    typename M::value_type dereference() const ;

    // Standard Boost.Iterator idiom
    friend class boost::iterator_core_access ;
} ;

// Beginning of container
template<typename M>
stl_map_iterator<M>::stl_map_iterator(const py::object& d, base_type& i)
    : stl_map_iterator::iterator_adaptor_(i),
      m_dict(d)
{}

// End of container
template<typename M>
stl_map_iterator<M>::stl_map_iterator()
{}

// Convert key to key-value pair
template<typename M>
typename M::value_type stl_map_iterator<M>::dereference() const
{
    key_type key = *(this->base_reference()) ;
    val_type val = py::extract<val_type>(m_dict[key]) ;
    return std::make_pair(key, val) ;
}

/*
    @brief Convert Python iterable objects to their C++ equivalents.

    This class provides an API for registering a Boost.Python converter
    for converting Python lists, tuples, dicts, etc. to C++ equivalents.
    It is meant to be used when we want to expose a C++ function that
    takes a list, map, etc. to Python and want its Python interface to
    accept the Python equivalent.

    @note This code is lifted almost verbatim from the following Stack
    Overflow post:

        http://stackoverflow.com/questions/15842126/feeding-a-python-list-into-a-function-taking-in-a-vector-with-boost-python

    @par
    However, the support for converting Python dicts to STL maps is a
    Minx-specific enhancement.
*/
struct iterable_converter {
    /*
        @brief  Register a Boost.Python converter to convert to C++ type C.
        @return Ref. to this object to allow chaining calls to from_python().

        This function registers a Boost.Python converter that will
        produce a C++ object of type C given its Python equivalent.
        Typically, the Python object will be a list or a dict and the
        type C will be an STL container such as list or map.

        You should call this function as part of the Boost.Python module
        initialization sequence. In Minx, this would be in the
        pythonize() function of a class being exposed to Python. For
        example, if you have a C++ function that takes an STL list of
        integers and you want to expose it to Python so that it can
        accept a Python list of ints, in the corresponding pythonize()
        function you would do something like this:

        @code
            iterable_converter().from_python<std::list<int> >() ;
        @endcode

        Continuing the above example, if you have another function that
        takes an STL map of ints to strings, the pythonize() function
        would be as follows:

        @code
            iterable_converter().from_python<std::list<int> >().
                                 from_python<std::map <int, std::string> >() ;
        @endcode
    */
    template<typename C>
    iterable_converter& from_python() {
        py::converter::registry::push_back(&iterable_converter::convertible,
                                           &iterable_converter::construct<C>,
                                           py::type_id<C>()) ;
        return *this ;
    }

private:
    // Check if supplied Python object supports Python's iterator protocol
    static void* convertible(PyObject* obj) {
        return PyObject_GetIter(obj) ? obj : 0 ;
    }

    /*
        @brief  Convert Python iterable object into C++ type C.
        @param  obj The Python iterable object to be converted.
        @param  dat Raw memory area for in-place construction of C.
        @return C++ container C via parameter dat.

        This function performs the Boost.Python black magic that iterates
        over the Python container obj and constructs the requested C++
        container (type C). Typically, C will be an STL list, vector, or
        map.
    */
    template<typename C>
    static void construct(PyObject* obj,
                          py::converter::rvalue_from_python_stage1_data* dat)
    {
        // For in-place construction of C++ container in the raw memory
        // area provided by Boost.Python, we need to be able view the raw
        // bytes as an object of type C...
        typedef py::converter::rvalue_from_python_storage<C> storage_type ;
        void* storage = reinterpret_cast<storage_type*>(dat)->storage.bytes ;

        // Python ref-count book-keeping
        py::handle<> handle(py::borrowed(obj)) ;
        py::object iterable(handle) ;

        // Converting a Python dict to an STL map needs special handling
        // because Boost.Python doesn't seem to provide a ready-made
        // (key, value) iterator, which requires us to roll our own...
        if (PyDict_Check(obj))
        {
            typedef typename C::value_type::first_type key_type ;
            typedef py::stl_input_iterator<key_type> key_iterator ;
            typedef stl_map_iterator<C> iterator ;
            key_iterator begin = key_iterator(iterable) ;
            dat->convertible = new(storage) C(iterator(iterable, begin),
                                              iterator()) ;
        }
        else // Boost.Python takes care of flat lists and tuples without a hitch
        {
            typedef py::stl_input_iterator<typename C::value_type> iterator ;
            dat->convertible = new(storage) C(iterator(iterable),
                                              iterator()) ;
        }
    }
} ;

//-----------------------------------------------------------------------

} // namespace minxlib

#endif // #ifndef MINXLIB_PYTHON_DOT_HH

/**********************************************/
/* Editor config:                             */

Added minxlib/root_window.cc.































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
    @file  root_window.cc
    @brief Implementation of API defined in root_window.hh.
*/

/*
    Copyright (C) 2012 The Minx Project Developers

    See wiki/copyright.wiki in the top-level directory of the Minx
    source distribution for the full list of authors who have
    contributed to this project.
*/

/*
    This file is part of Minx.

    Minx is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Minx is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
    License for more details.

    You should have received a copy of the GNU General Public License
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "root_window.hh"
#include "logging.hh"
#include "python.hh"

// Xlib
#include <X11/Xlib.h>

// Standard C++
#include <sstream>
#include <algorithm>
#include <iterator>

//-------------------------- INITIALIZATION ----------------------------

namespace minxlib {

static logging logger ;

// Quick helper to dump geometry to Minx log
static void dump_geometry(const std::vector<int>& geom, Window id, int screen)
{
    std::ostringstream str ;
    std::copy(geom.begin(), geom.end(), std::ostream_iterator<int>(str, " ")) ;
    logger.debug() << "screen " <<  screen  << "'s root window (" << id
                   << ") geometry = [" << str.str() << ']' ;
}

// Root window constructor when not using Xinerama
root_window::root_window(Display* d, int s)
    : window(d, RootWindow(d, s)),
      m_screen(s),
      m_geom(window::geometry())
{
    dump_geometry(m_geom, m_id, m_screen) ;
}

#ifdef MINXLIB_HAS_XINERAMA
// Root window constructor when using Xinerama
root_window::root_window(Display* d, int s, const XineramaScreenInfo& i)
    : window(d, RootWindow(d, 0)),
      m_screen(s)
{
    logger.info() << "setting screen " << s
                  << "'s root window ("<< m_id
                  << ") geometry from Xinerama";

    m_geom.reserve(5) ;
    m_geom.push_back(i.x_org) ;
    m_geom.push_back(i.y_org) ;
    m_geom.push_back(i.width) ;
    m_geom.push_back(i.height);
    m_geom.push_back(0) ;

    dump_geometry(m_geom, m_id, m_screen) ;
}
#endif // MINXLIB_HAS_XINERAMA

//--------------------------- PROPERTIES ------------------------------

void root_window::set_properties(const std::map<std::string, std::string>&)
{
    logger.warning() << "attempting to set properties on screen "
                     << m_screen << "'s root window " << m_id ;
}

//-------------------- PARENT-CHILD RELATIONSHIPS -----------------------

void root_window::reparent(const window&)
{
    logger.warning() << "attempting to reparent screen " << m_screen
                     << "'s root window " << m_id ;
}

window root_window::parent()
{
    logger.warning() << "attempting to get parent of screen " << m_screen
                     << "'s root window " << m_id ;
    return window(m_display, 0) ;
}

int root_window::screen()
{
    return m_screen ;
}

//----------------------- WINDOW VISIBILITY ----------------------------

void root_window::show()
{
    logger.warning() << "attempting to show screen " << m_screen
                     << "'s root window " << m_id ;
}

void root_window::hide()
{
    logger.warning() << "attempting to hide screen " << m_screen
                     << "'s root window " << m_id ;
}

bool root_window::is_mapped()
{
    return true ;
}

//------------------------ WINDOW GEOMETRY -----------------------------

void root_window::move_resize(int, int, int, int)
{
    logger.warning() << "attempting to move/resize screen " << m_screen
                     << "'s root window " << m_id ;
}

void
root_window::
configure(int, int, int, int, int, const window*, int, unsigned int)
{
    logger.warning() << "attempting to configure screen " << m_screen
                     << "'s root window " << m_id ;
}

void root_window::set_border_attr(unsigned long, unsigned int)
{
    logger.warning() << "attempting to set border attributes on screen "
                     << m_screen << "'s root window " << m_id ;
}

std::vector<int> root_window::geometry()
{
    return m_geom ;
}

//---------------------------- INPUT FOCUS ------------------------------

void root_window::focus()
{
    logger.warning() << "attempting to focus screen " << m_screen
                     << "'s root window " << m_id ;
}

//----------------------- WINDOW DESTRUCTION ---------------------------

void root_window::kill()
{
    logger.warning() << "attempting to kill screen " << m_screen
                     << "'s root window " << m_id ;
}

void root_window::nuke()
{
    logger.warning() << "attempting to nuke screen " << m_screen
                     << "'s root window " << m_id ;
}

//------------------------ PYTHON INTERFACE ----------------------------

void root_window::pythonize()
{
    py::class_<root_window, py::bases<window> >("root_window", py::no_init).
        def("set_properties",   &root_window::set_properties ).
        def("reparent",         &root_window::reparent       ).
        def("parent",           &root_window::parent         ).
        def("screen",           &root_window::screen         ).
        def("show",             &root_window::show           ).
        def("hide",             &root_window::hide           ).
        def("is_mapped",        &root_window::is_mapped      ).
        def("move_resize",      &root_window::move_resize    ).
        def("configure",        &root_window::configure      ).
        def("set_border_attr",  &root_window::set_border_attr).
        def("geometry",         &root_window::geometry       ).
        def("focus",            &root_window::focus          ).
        def("kill",             &root_window::kill           ).
        def("nuke",             &root_window::nuke           );

    logger = logging::get_logger("root_window") ;
}

} // namespace minxlib

//----------------------------------------------------------------------

/**********************************************/
/* Editor config:                             */
/**********************************************/
/* Local Variables:                           */
/* indent-tabs-mode: nil                      */
/* c-basic-offset: 4                          */
/* End:                                       */
/**********************************************/
/* vim: set expandtab shiftwidth=4 tabstop=4: */
/**********************************************/

Added minxlib/root_window.hh.











































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/**
    @file  root_window.hh
    @brief Encapsulation of an X root window.

    This file defines a class that wraps around the notion of an X
    root window and provides a Python interface for the rest of Minx.
*/

/*
    Copyright (C) 2012 The Minx Project Developers

    See wiki/copyright.wiki in the top-level directory of the Minx
    source distribution for the full list of authors who have
    contributed to this project.
*/

/*
    This file is part of Minx.

    Minx is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Minx is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
    License for more details.

    You should have received a copy of the GNU General Public License
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef MINXLIB_ROOT_WINDOW_DOT_HH
#define MINXLIB_ROOT_WINDOW_DOT_HH

//----------------------------- HEADERS --------------------------------

// Minx
#include "window.hh"

// Xlib
#ifdef MINXLIB_HAS_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif

// Standard C++
#include <vector>

//------------------------- CLASS DEFINITION ---------------------------

namespace minxlib {

/**
    @brief Encapsulate the details of an X root window.

    This class provides an API for the Python parts of Minx to be able to
    deal with X root windows. It wraps around the relevant parts of Xlib
    and exposes its functionality to Python via @boostpylink.

    Although we could just represent a root window as just another
    minxlib::window, we have a dedicated subclass for root windows to be
    able to deal with Xinerama. When Xinerama is active, a multi-head
    setup will have just one root window. However, to make Minx's layout
    functionality work properly across multiple physical monitors,
    minxlib always presents as many root windows as there are physical
    monitors. With Xinerama, these root windows will have the same window
    ID, but will have different geometries.
*/
class root_window: public window {
    // When we create a root window, we specify which screen it is on.
    int m_screen ;

    // Root windows have fixed geometry.
    std::vector<int> m_geom ;

public:
    /**
        @brief  Create a wrapper object for a root window.
        @param  d The display object to which the window is "linked."
        @param  s The zero-based screen index whose root window we want.
        @return A valid wrapper object for the specified screen's root window.

        This constructor is meant to be used when Xinerama is not active.
        In multi-head setups without Xinerama, the X server will be
        configured with independent displays.
    */
    root_window(Display* d, int s) ;

#ifdef MINXLIB_HAS_XINERAMA
    /**
        @brief  Create a wrapper object for a root window with Xinerama active.
        @param  d The display object to which the window is "linked."
        @param  s The zero-based screen index whose root window we want.
        @param  i The Xinerama screen info structure for the specified screen.
        @return A valid wrapper object for the specified screen's root window.

        This constructor is meant to be used when Xinerama is active. It
        is the caller's responsibility to obtain the XineramaScreenInfo
        structures by calling XineramaQueryScreens() and then freeing
        them once all the necessary root window objects have been
        created.
    */
    root_window(Display* d, int s, const XineramaScreenInfo& i) ;
#endif

    /**
        @brief  Export the window class to minxlib Python module.
        @return Nothing.

        This function exposes the root window class's interface so that
        it can be used by the Python parts of Minx. It is meant to be
        called by the Boost.Python initialization code in python.cc.
    */
    static void pythonize() ;

    /**
        @brief  Set window properties.
        @param  prop An STL map of strings to strings.
        @return Nothing.

        This is an override of minxlib::window::set_properties(). minxlib
        does not allow root window properties to be set. Thus, this
        implementation simply logs a warning to the Minx log and does
        nothing else.
    */
    void set_properties(const std::map<std::string, std::string>& prop) ;

    /**
        @brief  Reparent this window to another.
        @param  p New parent window.
        @return Nothing.

        This is an override of minxlib::window::reparent(). minxlib does
        not allow root windows to be reparented. Thus, this
        implementation simply logs a warning to the Minx log and does
        nothing else.
    */
    void reparent(const window& p) ;

    /**
        @brief  Get this root window's parent window.
        @return Window with ID zero.

        This is an override of minxlib::window::parent(). minxlib does
        not allow querying root windows for their parents. Thus, this
        implementation simply logs a warning to the Minx log and returns
        a window object with ID zero.
    */
    window parent() ;

    /**
        @brief  Get screen number of this root window.
        @return Root window's screen number.

        This function simply returns the screen index used when this root
        window object was created.
    */
    int screen() ;

    /**
        @brief  Show the window, i.e., map it.
        @return Nothing.

        This is an override of minxlib::window::show(). Since root
        windows are always visible, this function simply logs a warning
        to the Minx log.
    */
    void show() ;

    /**
        @brief  Hide this window, i.e., unmap it.
        @return Nothing.

        This is an override of minxlib::window::hide(). Since root
        windows are always visible and cannot be unmapped, this function
        simply logs a warning to the Minx log.
    */
    void hide() ;

    /**
        @brief  Check if root window is currently mapped or not.
        @return True (root windows are always mapped).
    */
    bool is_mapped() ;

    /**
        @brief  Move and resize the window.
        @param  x Window's x coordinate relative to parent's origin.
        @param  y Window's y coordinate relative to parent's origin.
        @param  w Window's width (not counting its border).
        @param  h Window's height (not counting its border).
        @return Nothing.

        This is an override of minxlib::window::move_resize(). minxlib
        does not allow root windows to be moved and/or resized. Thus,
        this implementation simply logs a warning to the Minx log and
        does nothing else.
    */
    void move_resize(int x, int y, int w, int h) ;

    /**
        @brief  Configure the window.
        @param  x Window's x coordinate relative to parent's origin.
        @param  y Window's y coordinate relative to parent's origin.
        @param  w Window's width (not counting its border).
        @param  h Window's height (not counting its border).
        @param  b Window's border width.
        @param  s Window's sibling for stacking operations.
        @param  t Window's stacking mode.
        @param  v Value mask to determine what to configure.
        @return Nothing.

        This is an override of minxlib::window::configure(). minxlib does
        not allow root windows to be configured. Thus, this
        implementation simply logs a warning to the Minx log and does
        nothing else.
    */
    void configure(int x, int y, int w, int h, int b,
                   const window* s, int t, unsigned int v) ;

    /**
        @brief  Set window's border color and size.
        @param  c Three-byte RGB spec.
        @param  s Border size (in pixels).
        @return Nothing.

        This is an override of minxlib::window::set_border_attr().
        minxlib does not allow root window borders to be changed. Thus,
        this implementation simply logs a warning to the Minx log and
        does nothing else.
    */
    void set_border_attr(unsigned long c, unsigned int s) ;

    /**
        @brief  Retrieve window's size, position, and border width.
        @return STL vector of ints containing window geometry.

        Root windows have a fixed geometry. When a root window object is
        constructed, we determine its geometry and store that in a data
        member. This function simply returns the above-mentioned data
        member, which is an STL vector of integers containing the
        following five values:

            @li Element 0: x-coordinate of root window's top-left corner
            @li Element 1: y-coordinate of root window's top-left corner
            @li Element 2: root window width
            @li Element 3: root window height
            @li Element 4: root window's border width (always zero)

        @note For non-Xinerama setups, this function simply calls
        minxlib::window::geometry(), which, in turn, calls
        XGetGeometry(). If the call to XGetGeometry() fails, this
        function will return an empty vector (and, eventually, the X
        server will raise a protocol error).

        @par
        On the Python side, the STL vector returned by this function
        will be converted into a Python list.
    */
    std::vector<int> geometry() ;

    /**
        @brief  Set input focus on this root window and raise it.
        @return Nothing.

        This is an override of minxlib::window::focus(). minxlib does not
        allow root windows to be focused. Thus, this implementation
        simply logs a warning to the Minx log and does nothing else.
    */
    void focus() ;

    /**
        @brief  Kill this window.
        @return Nothing.

        This is an override of minxlib::window::kill(). minxlib does not
        allow root windows to be killed. Thus, this implementation simply
        logs a warning to the Minx log and does nothing else.
    */
    void kill() ;

    /**
        @brief  Kill this window using brute force.
        @return Nothing.

        This is an override of minxlib::window::nuke(). minxlib does not
        allow root windows to be killed. Thus, this implementation simply
        logs a warning to the Minx log and does nothing else.
    */
    void nuke() ;
} ; // class window

} // namespace minxlib

//----------------------------------------------------------------------

#endif // #ifndef MINXLIB_ROOT_WINDOW_DOT_HH

/**********************************************/
/* Editor config:                             */
/**********************************************/
/* Local Variables:                           */
/* indent-tabs-mode: nil                      */
/* c-basic-offset: 4                          */
/* End:                                       */
/**********************************************/
/* vim: set expandtab shiftwidth=4 tabstop=4: */
/**********************************************/

Changes to minxlib/util.cc.

216
217
218
219
220
221
222

































223
224
225
226
227
228
229
    KeyCode*   k = std::find(beg, end, keycode) ;
    if (k == end) // keycode is not in modifier map
        return 0U ;

    // If we get here, the modifier map does have the requested key
    return (1U << (static_cast<int>(k - beg)/modmap->max_keypermod)) & 0xFF ;
}


































} // namespace minxlib

//----------------------------------------------------------------------

/**********************************************/
/* Editor config:                             */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    KeyCode*   k = std::find(beg, end, keycode) ;
    if (k == end) // keycode is not in modifier map
        return 0U ;

    // If we get here, the modifier map does have the requested key
    return (1U << (static_cast<int>(k - beg)/modmap->max_keypermod)) & 0xFF ;
}

//---------------------------- RECTANGLES -------------------------------

rect::rect()
    : left(0), right(0), bottom(0), top(0)
{}

rect::rect(int x, int y, int w, int h)
    : left(x), right(x + w), bottom(y + h), top(y)
{}

bool rect::intersects(const rect& r) const
{
    return !(left   > r.right || right < r.left ||
             bottom < r.top   || top   > r.bottom) ;
}

rect rect::intersection(const rect& r) const
{
    rect x ;
    if (this->intersects(r)) {
        x.left   = std::max(left,   r.left) ;
        x.right  = std::min(right,  r.right ) ;
        x.bottom = std::max(bottom, r.bottom) ;
        x.top    = std::min(top,    r.top) ;
    }
    return x ;
}

int rect::area() const
{
    return (right - left) * (bottom - top) ;
}

} // namespace minxlib

//----------------------------------------------------------------------

/**********************************************/
/* Editor config:                             */

Changes to minxlib/util.hh.

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
..
64
65
66
67
68
69
70
71





















































72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
    You should have received a copy of the GNU General Public License
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef MINXLIB_UTIL_DOT_HH
#define MINXLIB_UTIL_DOT_HH

//----------------------------- HEADERS --------------------------------

// Xlib
#include <X11/Xlib.h>

// Standard C++
#include <string>

// Standard C
#include <stdint.h>

//----------------------- Xlib ERROR PRINTING --------------------------

namespace minxlib {

/**
    @brief  Return a string representation of X protocol errors.
    @param  e Xlib error structure containing the details of the protocol error.
    @return A string describing the error.
................................................................................
    @return Mask for identifying modifier within modifier keymap.

    This function checks if the given key is part of the X modifier map.
    If so, it will return a mask that will have a one in the bit
    position corresponding to that modifier. Otherwise, it returns zero.
*/
uint32_t modmask(KeySym k, XModifierKeymap* m, Display* d) ;






















































} // namespace minxlib

//----------------------------------------------------------------------

#endif // #ifndef MINXLIB_UTIL_DOT_HH

/**********************************************/
/* Editor config:                             */
/**********************************************/
/* Local Variables:                           */
/* indent-tabs-mode: nil                      */
/* c-basic-offset: 4                          */
/* End:                                       */
/**********************************************/
/* vim: set expandtab shiftwidth=4 tabstop=4: */
/**********************************************/







|










|







 








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|













27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
    You should have received a copy of the GNU General Public License
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef MINXLIB_UTIL_DOT_HH
#define MINXLIB_UTIL_DOT_HH

//------------------------------ HEADERS --------------------------------

// Xlib
#include <X11/Xlib.h>

// Standard C++
#include <string>

// Standard C
#include <stdint.h>

//------------------------------- XLIB ----------------------------------

namespace minxlib {

/**
    @brief  Return a string representation of X protocol errors.
    @param  e Xlib error structure containing the details of the protocol error.
    @return A string describing the error.
................................................................................
    @return Mask for identifying modifier within modifier keymap.

    This function checks if the given key is part of the X modifier map.
    If so, it will return a mask that will have a one in the bit
    position corresponding to that modifier. Otherwise, it returns zero.
*/
uint32_t modmask(KeySym k, XModifierKeymap* m, Display* d) ;

//---------------------------- RECTANGLES -------------------------------

/**
    @brief Helper class for rectangle intersections.

    This class represents a rectangle aligned with the principal axes
    with x increasing from left to right and y from top to bottom. The
    main purpose of this class is to find the intersections between
    windows.
*/
class rect {
    // The four edges of the rectangle.
    int left, right, bottom, top ;

    // Private default constructor to create an empty rectangle.
    rect() ;

public:
    /**
        @brief  Create a rectangle object.
        @param  x x-coordinate of top-left corner.
        @param  y y-coordinate of top-left corner.
        @param  w Rectangle width.
        @param  h Rectangle height.
        @return Rectangle object.
    */
    rect(int x, int y, int w, int h) ;

    /**
        @brief  Does this rectangle intersect with another?
        @param  r The other rectangle.
        @return True if this rectangle intersects r; false otherwise.
    */
    bool intersects(const rect& r) const ;

    /**
        @brief  Compute intersection of this rectangle with another.
        @param  r The other rectangle.
        @return Intersection of this rectangle with r.

        This method returns the intersection of this rectangle object
        with the rectangle r. If there is no intersection, the returned
        rectangle will be empty (i.e., zero width and height).
    */
    rect intersection(const rect& r) const ;

    /**
        @brief  What is the area of this rectangle?
        @return Area of rectangle.
    */
    int area() const ;
} ;

} // namespace minxlib

//-----------------------------------------------------------------------

#endif // #ifndef MINXLIB_UTIL_DOT_HH

/**********************************************/
/* Editor config:                             */
/**********************************************/
/* Local Variables:                           */
/* indent-tabs-mode: nil                      */
/* c-basic-offset: 4                          */
/* End:                                       */
/**********************************************/
/* vim: set expandtab shiftwidth=4 tabstop=4: */
/**********************************************/

Changes to minxlib/window.cc.

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42



43
44
45
46
47
48
49
..
50
51
52
53
54
55
56



57
58
59
60
61
62
63
64
65


66
67
68
69
70
71
72
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110





































































































111
112

113


114
115
116












































117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
...
141
142
143
144
145
146
147
148
149
150
151
152
153
154


155
156

157
158

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177







178
179
180
181
182
183
184
...
188
189
190
191
192
193
194

195
196






















197
198
199
200
201
202
203
204
205






206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264



265
266
267






268
269



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
...
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354





355
356
357

358
359
360
361
362
363
364
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "window.hh"
#include "display.hh"
#include "logging.hh"
#include "python.hh"
#include "keymap.hh"
#include "util.hh"

// Xlib
#include <X11/Xutil.h>




// Boost
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

#include <boost/foreach.hpp>

................................................................................
// Standard C++
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <iterator>




//-------------------------- INITIALIZATION ----------------------------

namespace minxlib {

static logging logger ;

window::window(display* d, Window w)
    : m_display(d), m_id(w)
{}



//--------------------------- PROPERTIES ------------------------------

// Convert strings in X text property to semicolon separated string
static std::string property_to_string(XTextProperty* prop)
{
    char** strings ; int n ;
................................................................................
    std::map<std::string, std::string> prop ;
    prop["name"     ] = "" ;
    prop["icon_name"] = "" ;
    prop["class"    ] = "" ;
    prop["res_name" ] = "" ;

    XTextProperty tp ;
    if (XGetWMName(m_display->xlib(), m_id, &tp))
        prop["name"] = property_to_string(&tp) ;
    else
        logger.warning() << "unable to get WM_NAME for window " << m_id ;

    if (XGetWMIconName(m_display->xlib(), m_id, &tp))
        prop["icon_name"] = property_to_string(&tp) ;
    else
        logger.warning() << "unable to get WM_ICON_NAME for window " << m_id ;

    XClassHint ch ;
    if (XGetClassHint(m_display->xlib(), m_id, &ch)) {
        prop["class"] = ch.res_class ;
        prop["res_name"] = ch.res_name ;
        XFree(ch.res_class) ;
        XFree(ch.res_name ) ;
    }





































































































    else
        logger.warning() << "unable to get WM_CLASS for window " << m_id ;




    return prop ;
}













































//----------------------------- EVENTS ---------------------------------

void window::select_events(long mask)
{
    using std::hex ; using std::setw ; using std::setfill ;
    logger.debug() << "setting input mask for window " << m_id
                   << " to 0x" << hex << setw(8) << setfill('0') << mask ;
    XSelectInput(m_display->xlib(), m_id, mask) ;
}

//-------------------- KEYBOARD/MOUSE GRABBING -------------------------

void window::grab_key(const std::string& key_binding)
{
    using std::dec ; using std::hex ; using std::setw ; using std::setfill ;
................................................................................
    std::string key = split.back() ;
    KeySym keysym = XStringToKeysym(key.c_str()) ;
    if (keysym == NoSymbol) {
        logger.warning() << "no keysym matching \"" << key << '"' ;
        return ;
    }

    KeyCode keycode = XKeysymToKeycode(m_display->xlib(), keysym) ;
    if (keycode == 0) {
        logger.warning() << "no keycode matching \"" << key << '"';
        return ;
    }
    logger.debug() << "keycode for \"" << key << "\" = "
                   << dec << static_cast<int>(keycode) ;



    // Setup map for modifier short-hands

    typedef std::map<std::string, unsigned int> modmap ;
    static modmap modifier_masks ;

    if (modifier_masks.empty()) {
        modifier_masks["C" ] = ControlMask ;
        modifier_masks["S" ] = ShiftMask;
        modifier_masks["M1"] = Mod1Mask ;
        modifier_masks["M2"] = Mod2Mask ;
        modifier_masks["M3"] = Mod3Mask ;
        modifier_masks["M4"] = Mod4Mask ;
        modifier_masks["M5"] = Mod5Mask ;

        // Now for ALT and META short-hands
        XModifierKeymap*  mkmap = XGetModifierMapping(m_display->xlib()) ;
        if (mkmap) {
            modifier_masks["A"] = modmask(XK_Alt_L , mkmap, m_display->xlib());
            modifier_masks["M"] = modmask(XK_Meta_L, mkmap, m_display->xlib());
            uint32_t mask =
                keymap::ignore_lock_modifiers(mkmap, m_display->xlib()) ;
            logger.debug() << "set modifier testing mask to 0x"
                           << hex << setw(4) << setfill('0') << mask ;
            XFreeModifiermap(mkmap) ;







        }
    }

    // First n-1 strings of split are the modifiers
    split.pop_back() ;
    unsigned int mask = 0 ;
    BOOST_FOREACH(const std::string& s, split)
................................................................................
            mask |= it->second ;
    }
    if (!mask)
        mask = AnyModifier ;
    logger.debug() << "modifier mask for \""   << key_binding << "\" = 0x"
                   << hex << setw(4) << setfill('0') << mask ;


    XGrabKey(m_display->xlib(), keycode, mask, m_id,
             False, GrabModeAsync, GrabModeAsync) ;






















    keymap::bind(keycode, mask, key_binding) ;
}

//----------------------- WINDOW VISIBILITY ----------------------------

void window::show()
{
    logger.debug() << "mapping window " << m_id ;
    XMapWindow(m_display->xlib(), m_id) ;






}

bool window::is_mapped()
{
    XWindowAttributes attr ;
    Status s = XGetWindowAttributes(m_display->xlib(), m_id, &attr) ;
    if (!s) {
        logger.error() << "unable to check map state for window " << m_id ;
        return false ; // throw exception instead?
    }
    return attr.map_state != IsUnmapped ;
}

//------------------------ WINDOW GEOMETRY -----------------------------

void window::move_resize(int x, int y, int w, int h)
{
    logger.debug() << "setting geometry for window " << m_id << " to "
                   << w << 'x' << h << '+' << x << '+' << y;
    XMoveResizeWindow(m_display->xlib(), m_id, x, y, w, h) ;
}

void window::configure(int x, int y, int width, int height,
                       int border_width,  const window* sibling,
                       int stacking_mode, unsigned int value_mask)
{
    using std::hex ; using std::setw ; using std::setfill ;
................................................................................
    wc.x = x ;
    wc.y = y ;
    wc.width  = width ;
    wc.height = height;
    wc.border_width = border_width ;
    wc.sibling      = sibling ? sibling->m_id : 0 ;
    wc.stack_mode   = stacking_mode;
    XConfigureWindow(m_display->xlib(), m_id, value_mask, &wc) ;
}

void window::set_border_attr(unsigned long c, unsigned int s)
{
    using std::hex ; using std::setw ; using std::setfill ;
    logger.debug() << "setting window " << m_id
                   << " border color = 0x" << hex << setw(6) << setfill('0')
                   << c
                   << ", size = " << s ;
    XSetWindowBorderWidth(m_display->xlib(), m_id, s) ;
    XSetWindowBorder     (m_display->xlib(), m_id, c) ;
}

boost::tuple<int, int, int, int> window::geometry()
{
    XWindowAttributes attr ;
    Status s = XGetWindowAttributes(m_display->xlib(), m_id, &attr) ;



    if (!s) {
        logger.error() << "failed to get window geometry for ID " << m_id ;
        return boost::make_tuple(-1, -1, -1, -1) ; // throw exception instead?






    }
    return boost::make_tuple(attr.x, attr.y, attr.width, attr.height) ;



}

//---------------------------- INPUT FOCUS ------------------------------

void window::focus()
{
    logger.debug() << "raising and focusing window " << m_id ;
    XRaiseWindow  (m_display->xlib(), m_id) ;
    XSetInputFocus(m_display->xlib(), m_id,
                   RevertToPointerRoot, CurrentTime) ;
}

//----------------------- WINDOW DESTRUCTION ---------------------------

// Check if specified window wants to participate in the
// WM_DELETE_WINDOW protocol. If so, fill out XEvent's
................................................................................
    }
    return dw ;
}

void window::kill()
{
    XEvent e ;
    if  (delete_window(m_display->xlib(), m_id, &e)) {
        if (XSendEvent(m_display->xlib(), m_id, False, 0L, &e)) {
            logger.debug() << "sent WM_DELETE_WINDOW to window " << m_id ;
            return ;
        }
        logger.warning() << "WM_DELETE_WINDOW on window " << m_id << " failed" ;
    }
    nuke() ;
}

void window::nuke()
{
    logger.debug() << "killing window " << m_id << " with brute force" ;
    XKillClient(m_display->xlib(), m_id) ;
}

//------------------------ PYTHON INTERFACE ----------------------------

static void register_converters()
{
    // STL map containing window properties to a Python dictionary
    typedef std::map<std::string, std::string> props ;
    py::to_python_converter<props, universal_converter<props> >() ;

    // Boost 4-tuple containing x, y, width, height into a Python tuple
    typedef boost::tuple<int, int, int, int> geom ;
    py::to_python_converter<geom, universal_converter<geom> >() ;
}

void window::pythonize()
{
    register_converters() ;

    py::class_<window>("window", py::no_init).
        def(py::self == py::self).
        def(py::self != py::self).
        def(py::self == py::other<Window>()  ).
        def(py::self != py::other<Window>()  ).
        def_readonly("id",      &window::m_id).
        def("properties",       &window::properties     ).





        def("select_events",    &window::select_events  ).
        def("grab_key",         &window::grab_key       ).
        def("show",             &window::show           ).

        def("is_mapped",        &window::is_mapped      ).
        def("move_resize",      &window::move_resize    ).
        def("configure",        &window::configure      ).
        def("set_border_attr",  &window::set_border_attr).
        def("geometry",         &window::geometry       ).
        def("focus",            &window::focus          ).
        def("kill",             &window::kill           ).







<







>
>
>







 







>
>
>
|





|


>
>







 







|

<
<

|

<
<


|





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
>

>
>
|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







|







 







|





|
>
>

|
>


>









|
|

|
|

|



>
>
>
>
>
>
>







 







>
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








|
>
>
>
>
>
>





|











|

|







 







|









|
|


|

|
|
>
>
>
|
<
<
>
>
>
>
>
>

<
>
>
>







|
|







 







|
|











|




<
<
<
<
<
<
<
<
<
<
<


<
<







>
>
>
>
>



>







28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
..
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
..
94
95
96
97
98
99
100
101
102


103
104
105


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
...
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
...
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458


459
460
461
462
463
464
465

466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
...
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531











532
533


534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
    along with Minx. If not, see <http://www.gnu.org/licenses/>.
*/

//----------------------------- HEADERS --------------------------------

// minxlib
#include "window.hh"

#include "logging.hh"
#include "python.hh"
#include "keymap.hh"
#include "util.hh"

// Xlib
#include <X11/Xutil.h>
#ifdef MINXLIB_HAS_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif

// Boost
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

#include <boost/foreach.hpp>

................................................................................
// Standard C++
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <iterator>

// Standard C
#include <string.h>

//-------------------- INITIALIZATION AND CLEAN-UP ----------------------

namespace minxlib {

static logging logger ;

window::window(Display* d, Window w)
    : m_display(d), m_id(w)
{}

window::~window(){}

//--------------------------- PROPERTIES ------------------------------

// Convert strings in X text property to semicolon separated string
static std::string property_to_string(XTextProperty* prop)
{
    char** strings ; int n ;
................................................................................
    std::map<std::string, std::string> prop ;
    prop["name"     ] = "" ;
    prop["icon_name"] = "" ;
    prop["class"    ] = "" ;
    prop["res_name" ] = "" ;

    XTextProperty tp ;
    if (XGetWMName(m_display, m_id, &tp))
        prop["name"] = property_to_string(&tp) ;



    if (XGetWMIconName(m_display, m_id, &tp))
        prop["icon_name"] = property_to_string(&tp) ;



    XClassHint ch ;
    if (XGetClassHint(m_display, m_id, &ch)) {
        prop["class"] = ch.res_class ;
        prop["res_name"] = ch.res_name ;
        XFree(ch.res_class) ;
        XFree(ch.res_name ) ;
    }

    return prop ;
}

void window::set_properties(const std::map<std::string, std::string>& prop)
{
    std::map<std::string, std::string>::const_iterator it = prop.find("name") ;
    if (it != prop.end()) {
        XTextProperty tp ;
        char* p = const_cast<char*>(it->second.c_str()) ;
        if (XStringListToTextProperty(&p, 1, &tp)) {
            XSetWMName(m_display, m_id, &tp) ;
            XFree(tp.value) ;
            logger.debug() << "set WM_NAME for window " << m_id
                           << " to " << it->second ;
        }
    }

    it = prop.find("icon_name") ;
    if (it != prop.end()) {
        XTextProperty tp ;
        char* p = const_cast<char*>(it->second.c_str()) ;
        if (XStringListToTextProperty(&p, 1, &tp)) {
            XSetWMIconName(m_display, m_id, &tp) ;
            XFree(tp.value) ;
            logger.debug() << "set WM_ICON_NAME for window " << m_id
                           << " to " << it->second ;
        }
    }

    XClassHint ch ;
    ch.res_class = 0 ;
    ch.res_name  = 0 ;
    it = prop.find("class") ;
    if (it != prop.end()) {
        ch.res_class = new char[it->second.length() + 1] ;
        strcpy(ch.res_class, it->second.c_str()) ;
    }
    it = prop.find("res_name") ;
    if (it != prop.end()) {
        ch.res_name = new char[it->second.length() + 1] ;
        strcpy(ch.res_name, it->second.c_str()) ;
    }
    if (ch.res_class || ch.res_name) {
        XSetClassHint(m_display, m_id, &ch) ;
        logger.debug() << "set WM_CLASS for window " << m_id
                       << " to (" << (ch.res_class ? ch.res_class : "NULL")
                       << ", "    << (ch.res_name  ? ch.res_name  : "NULL")
                       << ')' ;
    }
    delete[] ch.res_class;
    delete[] ch.res_name ;
}

//-------------------- PARENT-CHILD RELATIONSHIPS -----------------------

void window::reparent(const window& new_parent)
{
    logger.debug() << "setting " << m_id << "'s parent to " << new_parent ;
    XReparentWindow(m_display, m_id, new_parent, 0, 0) ;
}

window window::parent()
{
    logger.debug() << "getting " << m_id << "'s parent" ;

    Window  root, parent_id ;
    Window* children = 0 ;
    unsigned int num_children ;
    Status s = XQueryTree(m_display, m_id,
                          &root, &parent_id, &children, &num_children) ;
    if (children) // whatever, don't care about these little buggers
        XFree(children) ;
    if (!s) {
        logger.error() << "unable to query tree for window " << m_id ;
        return window(m_display, 0) ; // throw exception instead?
    }

    logger.debug() << "window " << m_id << "'s parent = " << parent_id ;
    return window(m_display, parent_id) ;
}

std::vector<window> window::children()
{
    logger.debug() << "getting " << m_id << "'s children" ;

    std::vector<window> windows ;

    Window  root, parent ;
    Window* children = 0 ;
    unsigned int num_children ;
    Status s = XQueryTree(m_display, m_id,
                          &root, &parent, &children, &num_children) ;
    if (s) {
        if (children) {
            windows.reserve(num_children) ;
            for (unsigned int i = 0; i < num_children; ++i)
                windows.push_back(window(m_display, children[i])) ;
            XFree(children) ;
        }
    }
    else

        logger.error() << "unable to query tree for window " << m_id ;

    logger.debug() << "window " << m_id << " has " << windows.size()
                   << " children" ;
    return windows ;
}

int window::screen()
{
    XWindowAttributes attr ;
    Status s = XGetWindowAttributes(m_display, m_id, &attr) ;
    if (!s) {
        logger.error() << "unable to get screen index for window " << m_id ;
        return -1 ; // throw exception instead?
    }

    int screen_number = -1 ;
#ifdef MINXLIB_HAS_XINERAMA
    int n = 0 ;
    XineramaScreenInfo* screens = XineramaQueryScreens(m_display, &n) ;
    if (n > 0)
    {
        logger.debug() << "Xinerama enabled; " << n << " screens" ;

        // Find the physical screen that has the most of this window
        int  max_area = -1 ;
        rect w(attr.x, attr.y, attr.width, attr.height) ;
        for (int i = 0; i < n; ++i)
        {
            rect r(screens[i].x_org, screens[i].y_org,
                   screens[i].width, screens[i].height) ;
            rect x(w.intersection(r)) ;
            int area = x.area() ;
            if (area > max_area)
            {
                max_area = area ;
                screen_number = i ;
            }
        }
        XFree(screens) ;
    }
    else
#endif
    {
        logger.debug() << "Xinerama unavailable or inactive" ;
        screen_number = XScreenNumberOfScreen(attr.screen) ;
    }
    logger.debug() << "window " << m_id << " on screen " << screen_number ;
    return screen_number ;
}

//----------------------------- EVENTS ---------------------------------

void window::select_events(long mask)
{
    using std::hex ; using std::setw ; using std::setfill ;
    logger.debug() << "setting input mask for window " << m_id
                   << " to 0x" << hex << setw(8) << setfill('0') << mask ;
    XSelectInput(m_display, m_id, mask) ;
}

//-------------------- KEYBOARD/MOUSE GRABBING -------------------------

void window::grab_key(const std::string& key_binding)
{
    using std::dec ; using std::hex ; using std::setw ; using std::setfill ;
................................................................................
    std::string key = split.back() ;
    KeySym keysym = XStringToKeysym(key.c_str()) ;
    if (keysym == NoSymbol) {
        logger.warning() << "no keysym matching \"" << key << '"' ;
        return ;
    }

    KeyCode keycode = XKeysymToKeycode(m_display, keysym) ;
    if (keycode == 0) {
        logger.warning() << "no keycode matching \"" << key << '"';
        return ;
    }
    logger.debug() << "keycode for \"" << key << "\" = "
                   << dec << static_cast<int>(keycode) << " (0x"
                   << hex << setw(4) << setfill('0')
                   << static_cast<int>(keycode) << ')' ;

    // Setup map for modifier short-hands and also initialize the list of
    // masks for the different lock modifiers.
    typedef std::map<std::string, unsigned int> modmap ;
    static modmap modifier_masks ;
    static std::vector<uint32_t> lock_masks;
    if (modifier_masks.empty()) {
        modifier_masks["C" ] = ControlMask ;
        modifier_masks["S" ] = ShiftMask;
        modifier_masks["M1"] = Mod1Mask ;
        modifier_masks["M2"] = Mod2Mask ;
        modifier_masks["M3"] = Mod3Mask ;
        modifier_masks["M4"] = Mod4Mask ;
        modifier_masks["M5"] = Mod5Mask ;

        // Now for ALT and META short-hands and the lock masks
        XModifierKeymap*  mkmap = XGetModifierMapping(m_display) ;
        if (mkmap) {
            modifier_masks["A"] = modmask(XK_Alt_L , mkmap, m_display) ;
            modifier_masks["M"] = modmask(XK_Meta_L, mkmap, m_display) ;
            uint32_t mask =
                keymap::ignore_lock_modifiers(mkmap, m_display) ;
            logger.debug() << "set modifier testing mask to 0x"
                           << hex << setw(4) << setfill('0') << mask ;
            XFreeModifiermap(mkmap) ;

            mask = ~mask & 0xFF ;
            logger.debug() << "lock masks = 0x"
                           << hex << setw(4) << setfill('0') << mask ;
            for (int i = 0; i < 8; ++i)
                if (mask & (1 << i))
                    lock_masks.push_back(1 << i) ;
        }
    }

    // First n-1 strings of split are the modifiers
    split.pop_back() ;
    unsigned int mask = 0 ;
    BOOST_FOREACH(const std::string& s, split)
................................................................................
            mask |= it->second ;
    }
    if (!mask)
        mask = AnyModifier ;
    logger.debug() << "modifier mask for \""   << key_binding << "\" = 0x"
                   << hex << setw(4) << setfill('0') << mask ;

    // Setup primary grab for key binding using plain modifier mask
    XGrabKey(m_display, keycode, mask, m_id,
             False, GrabModeAsync, GrabModeAsync) ;

    // Setup additional grabs with modifiers augmented with lock masks
    if (mask != AnyModifier) {
        unsigned int n = (1 << lock_masks.size()) - 1 ;
        for (unsigned int i = 1; i <= n; ++i) {
            uint32_t  lock_mask = 0;
            for (unsigned int j = 0; j < lock_masks.size(); ++j)
                if (i & (1 << j))
                    lock_mask |= lock_masks[j] ;

            logger.debug() << "setting up additional grab for \""
                           << key_binding << "\" with modifier mask 0x"
                           << hex << setw(4) << setfill('0')
                           << (mask | lock_mask) ;
            XGrabKey(m_display, keycode, mask | lock_mask, m_id,
                     False, GrabModeAsync, GrabModeAsync) ;
        }
    }

    // Store mapping between grab and key binding name so we can
    // translate keyboard events to the named key bindings as specified
    // by Minx's end-users.
    keymap::bind(keycode, mask, key_binding) ;
}

//----------------------- WINDOW VISIBILITY ----------------------------

void window::show()
{
    logger.debug() << "mapping window " << m_id ;
    XMapWindow(m_display, m_id) ;
}

void window::hide()
{
    logger.debug() << "unmapping window " << m_id ;
    XUnmapWindow(m_display, m_id) ;
}

bool window::is_mapped()
{
    XWindowAttributes attr ;
    Status s = XGetWindowAttributes(m_display, m_id, &attr) ;
    if (!s) {
        logger.error() << "unable to check map state for window " << m_id ;
        return false ; // throw exception instead?
    }
    return attr.map_state != IsUnmapped ;
}

//------------------------ WINDOW GEOMETRY -----------------------------

void window::move_resize(int x, int y, int w, int h)
{
    logger.debug() << "setting geometry for window "   << m_id << " to "
                   << w << 'x' << h << '+' << x << '+' << y;
    XMoveResizeWindow(m_display, m_id, x, y, w, h) ;
}

void window::configure(int x, int y, int width, int height,
                       int border_width,  const window* sibling,
                       int stacking_mode, unsigned int value_mask)
{
    using std::hex ; using std::setw ; using std::setfill ;
................................................................................
    wc.x = x ;
    wc.y = y ;
    wc.width  = width ;
    wc.height = height;
    wc.border_width = border_width ;
    wc.sibling      = sibling ? sibling->m_id : 0 ;
    wc.stack_mode   = stacking_mode;
    XConfigureWindow(m_display, m_id, value_mask, &wc) ;
}

void window::set_border_attr(unsigned long c, unsigned int s)
{
    using std::hex ; using std::setw ; using std::setfill ;
    logger.debug() << "setting window " << m_id
                   << " border color = 0x" << hex << setw(6) << setfill('0')
                   << c
                   << ", size = " << s ;
    XSetWindowBorderWidth(m_display, m_id, s) ;
    XSetWindowBorder     (m_display, m_id, c) ;
}

std::vector<int> window::geometry()
{
    std::vector<int> geom ;
    Window r ;
    int x, y ;
    unsigned int w, h, b, d ;
    Status s = XGetGeometry(m_display, m_id, &r, &x, &y, &w, &h, &b, &d) ;
    if (s) {


        geom.reserve(5) ;
        geom.push_back(x) ;
        geom.push_back(y) ;
        geom.push_back(w) ;
        geom.push_back(h) ;
        geom.push_back(b) ;
    }

    else
        logger.error() << "failed to get window geometry for ID " << m_id;
    return geom ;
}

//---------------------------- INPUT FOCUS ------------------------------

void window::focus()
{
    logger.debug() << "raising and focusing window " << m_id ;
    XRaiseWindow  (m_display, m_id) ;
    XSetInputFocus(m_display, m_id,
                   RevertToPointerRoot, CurrentTime) ;
}

//----------------------- WINDOW DESTRUCTION ---------------------------

// Check if specified window wants to participate in the
// WM_DELETE_WINDOW protocol. If so, fill out XEvent's
................................................................................
    }
    return dw ;
}

void window::kill()
{
    XEvent e ;
    if  (delete_window(m_display, m_id, &e)) {
        if (XSendEvent(m_display, m_id, False, 0L, &e)) {
            logger.debug() << "sent WM_DELETE_WINDOW to window " << m_id ;
            return ;
        }
        logger.warning() << "WM_DELETE_WINDOW on window " << m_id << " failed" ;
    }
    nuke() ;
}

void window::nuke()
{
    logger.debug() << "killing window " << m_id << " with brute force" ;
    XKillClient(m_display, m_id) ;
}

//------------------------ PYTHON INTERFACE ----------------------------












void window::pythonize()
{


    py::class_<window>("window", py::no_init).
        def(py::self == py::self).
        def(py::self != py::self).
        def(py::self == py::other<Window>()  ).
        def(py::self != py::other<Window>()  ).
        def_readonly("id",      &window::m_id).
        def("properties",       &window::properties     ).
        def("set_properties",   &window::set_properties ).
        def("reparent",         &window::reparent       ).
        def("parent",           &window::parent         ).
        def("children",         &window::children       ).
        def("screen",           &window::screen         ).
        def("select_events",    &window::select_events  ).
        def("grab_key",         &window::grab_key       ).
        def("show",             &window::show           ).
        def("hide",             &window::hide           ).
        def("is_mapped",        &window::is_mapped      ).
        def("move_resize",      &window::move_resize    ).
        def("configure",        &window::configure      ).
        def("set_border_attr",  &window::set_border_attr).
        def("geometry",         &window::geometry       ).
        def("focus",            &window::focus          ).
        def("kill",             &window::kill           ).

Changes to minxlib/window.hh.

35
36
37
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
...
130
131
132
133
134
135
136









137
138
139
140
141
142
143
...
163
164
165
166
167
168
169

















































































170
171
172
173
174
175
176
...
318
319
320
321
322
323
324
325






326
327
328
329
330
331
332
...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436

437
438
439

440
441
442
443
444

445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480













481
482
483
484
485
486
487
#define MINXLIB_WINDOW_DOT_HH

//----------------------------- HEADERS --------------------------------

// Xlib
#include <X11/Xlib.h>

// Boost
#include <boost/tuple/tuple.hpp>

// Standard C++
#include <ostream>
#include <map>

#include <string>

//------------------------- CLASS DEFINITION ---------------------------

namespace minxlib {

// Forward declaration
class display ;

/**
    @brief Encapsulate the details of an X window.

    This class provides an API for the Python parts of Minx to be able
    to deal with X windows. It wraps around the relevant parts of Xlib
    and exposes its functionality to Python via @boostpylink.
*/
class window {

    // All Xlib calls will require a display object...
    display* m_display ;

    // A window object's basic purpose is to wrap around the Xlib Window
    // data structure.
    Window m_id ;

public:
    /**
        @brief  Create a wrapper object for an existing Xlib window.
        @param  d The display object to which the window is "linked."
        @param  w The Xlib Window ID.
        @return A valid window wrapper for the given Xlib window.

        Minx does not create any X windows of its own. It only manages
        existing (top-level) windows. That's why we need the Xlib Window
        ID when instantiating this class.
    */
    window(display* d, Window w) ;

    /**
        @brief  Export the window class to minxlib Python module.
        @return Nothing.

        This function exposes the window class's interface so that it
        can be used by the Python parts of Minx. It is meant to be
................................................................................
        @return True if this window's ID does not equal w; false otherwise.

        This version of the inequality operator only checks this
        object's window ID against w.
    */
    bool operator!=(Window w) const {return m_id != w ;}










    /**
        @brief  Retrieve window properties from X server.
        @return An STL map of strings to strings.

        This function gets the WM_NAME, WM_ICON_NAME, and WM_CLASS
        properties for this window and returns them in an STL map of
        strings to strings as shown below:
................................................................................
        these multiple values in the returned map/dict will be
        represented as semicolon separated strings. For example, if some
        window's WM_NAME property has values "foo" and "bar", the value
        of the "name" key in the returned map will be "foo;bar".
    */
    std::map<std::string, std::string> properties() ;


















































































    /**
        @brief An enumeration for the different event masks.

        This enumeration simply provides C++ names for the Xlib event
        masks that are exported to Python via @boostpylink's enum
        exporting facility.

................................................................................
    */
    void grab_key(const std::string& s) ;

    /**
        @brief  Show the window, i.e., map it.
        @return Nothing.
    */
    void show() ;







    /**
        @brief  Check if window is currently mapped or not.
        @return True if window is mapped, false otherwise.

        This function uses XGetWindowAttributes() and checks the value
        of the XWindowAttributes's map_state field to see if the window
................................................................................
        IsViewable or IsUnviewable and false if either map_state is
        IsUnmapped or if the call to XGetWindowAttributes() fails.

        @note If XGetWindowAttributes() fails, we would expect a
        minxlib::protocol_error to be raised soon after. That's why this
        function returns false instead of throwing an exception.
    */
    bool is_mapped() ;

    /**
        @brief  Move and resize the window.
        @param  x Window's x coordinate relative to parent's origin.
        @param  y Window's y coordinate relative to parent's origin.
        @param  w Window's width (not counting its border).
        @param  h Window's height (not counting its border).
        @return Nothing.

        This function sets the window geometry by calling
        XMoveResizeWindow(). Please consult Xlib documentation for
        further details.
    */
    void move_resize(int x, int y, int w, int h) ;

    /**
        @brief  An enumeration for the different configure values.

        This enumeration simply provides C++ names for the Xlib value
        mask for configure requests. These names are exported to Python
        via @boostpylink's enum exporting facility.
................................................................................
        bitwise OR of the configure_mask enums.

        Please consult Xlib documentation for further details. For
        example, the following page may be instructive:

            http://tronche.com/gui/x/xlib/window/configure.html
    */
    void configure(int x, int y, int w, int h, int b,
                   const window* s, int t, unsigned int v) ;

    /**
        @brief  Set window's border color and size.
        @param  c Three-byte RGB spec.
        @param  s Border size (in pixels).
        @return Nothing.

        This function calls XSetWindowBorderWidth() and
        XSetWindowBorder() to specify the window's border color and size
        to the desired values.
    */
    void set_border_attr(unsigned long c, unsigned int s) ;

    /**
        @brief  Retrieve window's size and position.
        @return Tuple containing window geometry.

        This function uses XGetWindowAttributes() to determine the
        window's size and position. It returns a 4-tuple containing the
        position and size as shown below:

            @li Element 0: x-coordinate of window's top-left corner
            @li Element 1: y-coordinate of window's top-left corner
            @li Element 2: window width
            @li Element 3: window height


        If the call to XGetWindowAttributes() fails, all the elements of
        the tuple will be -1.


        @note On the Python size, the Boost tuple returned by this
        function will be converted into a Python tuple. Also, since we
        would expect a minxlib::protocol_error to be raised soon after a
        failed XGetWindowAttributes(), we return -1's instead of

        throwing an exception in case the call to XGetWindowAttributes()
        bombs.
    */
    boost::tuple<int, int, int, int> geometry() ;

    /**
        @brief  Set input focus on this window and raise it.
        @return Nothing.
    */
    void focus() ;

    /**
        @brief  Kill this window.
        @return Nothing.

        This method destroys the X window encapsulated by this object by
        killing the X client application that created the window. If the
        window supports the WM_DELETE_WINDOW protocol, this method will
        use that to kill the window and its client application.
        Otherwise, it will simply use the XKillClient() function to
        destroy the window and its client by brute force.
    */
    void kill() ;

    /**
        @brief  Kill this window using brute force.
        @return Nothing.

        This method destroys the X window encapsulated by this object
        without trying a graceful shutdown via WM_DELETE_WINDOW. It is
        meant to be used on those clients whose windows advertise
        support for WM_DELETE_WINDOW but don't implement the delete
        protocol properly and stay alive without good cause despite a
        user-initiated kill request.
    */
    void nuke() ;













} ; // class window

} // namespace minxlib

//----------------------------------------------------------------------

#endif // #ifndef MINXLIB_WINDOW_DOT_HH







<
<
<



>






<
<
<








>

|











<
<
<
<

|







 







>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>
>
>
>
>
>







 







|













|







 







|
|











|


|
|

|
|
|





>

<
|
>

|
|

<
>
|
<

|





|












|












|
>
>
>
>
>
>
>
>
>
>
>
>
>







35
36
37
38
39
40
41



42
43
44
45
46
47
48
49
50
51



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73




74
75
76
77
78
79
80
81
82
...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
...
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526

527
528
529
530
531
532

533
534

535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
#define MINXLIB_WINDOW_DOT_HH

//----------------------------- HEADERS --------------------------------

// Xlib
#include <X11/Xlib.h>




// Standard C++
#include <ostream>
#include <map>
#include <vector>
#include <string>

//------------------------- CLASS DEFINITION ---------------------------

namespace minxlib {




/**
    @brief Encapsulate the details of an X window.

    This class provides an API for the Python parts of Minx to be able
    to deal with X windows. It wraps around the relevant parts of Xlib
    and exposes its functionality to Python via @boostpylink.
*/
class window {
protected:
    // All Xlib calls will require a display object...
    Display* m_display ;

    // A window object's basic purpose is to wrap around the Xlib Window
    // data structure.
    Window m_id ;

public:
    /**
        @brief  Create a wrapper object for an existing Xlib window.
        @param  d The display object to which the window is "linked."
        @param  w The Xlib Window ID.
        @return A valid window wrapper for the given Xlib window.




    */
    window(Display* d, Window w) ;

    /**
        @brief  Export the window class to minxlib Python module.
        @return Nothing.

        This function exposes the window class's interface so that it
        can be used by the Python parts of Minx. It is meant to be
................................................................................
        @return True if this window's ID does not equal w; false otherwise.

        This version of the inequality operator only checks this
        object's window ID against w.
    */
    bool operator!=(Window w) const {return m_id != w ;}

    /**
        @brief  Convert to an Xlib Window.
        @return This object cast to an Xlib Window.

        This cast operator allows us to pass minxlib::window objects
        seamlessly to Xlib functions.
    */
    operator Window() const {return m_id ;}

    /**
        @brief  Retrieve window properties from X server.
        @return An STL map of strings to strings.

        This function gets the WM_NAME, WM_ICON_NAME, and WM_CLASS
        properties for this window and returns them in an STL map of
        strings to strings as shown below:
................................................................................
        these multiple values in the returned map/dict will be
        represented as semicolon separated strings. For example, if some
        window's WM_NAME property has values "foo" and "bar", the value
        of the "name" key in the returned map will be "foo;bar".
    */
    std::map<std::string, std::string> properties() ;

    /**
        @brief  Set window properties.
        @param  prop An STL map of strings to strings.
        @return Nothing.

        This function sets the window properties specified in its prop
        parameter. The following properties are supported:

            <table>
                <tr>
                    <th><b>minxlib Name</b></th>
                    <th><b>Xlib Property</b></th>
                </tr>
                <tr><td>name      </td>  <td>WM_NAME           </td></tr>
                <tr><td>icon_name </td>  <td>WM_ICON_NAME      </td></tr>
                <tr><td>class     </td>  <td>WM_CLASS.res_class</td></tr>
                <tr><td>res_name  </td>  <td>WM_CLASS.res_name </td></tr>
            </table>

        For example, let's say you have a minxlib::window object
        <tt>w</tt> and set its properties as shown by the snippet of code
        below:

        @code
            std::map<std::string, std::string> prop ;
            prop["class"] = "foo" ;
            prop["name" ] = "bar" ;
            w.set_properties(prop);
        @endcode

        That will set the WM_CLASS.res_class and WM_NAME properties of
        the window <tt>w</tt>.
    */
    virtual void set_properties(const std::map<std::string, std::string>& prop);

    /**
        @brief  Reparent this window to another.
        @param  p New parent window.
        @return Nothing.
    */
    virtual void reparent(const window& p) ;

    /**
        @brief  Get this window's parent window.
        @return Parent window.

        This function uses XQueryTree() to determine the parent window of
        the X window encapsulated by this object. If the call to
        XQueryTree() fails, this function will return a window object
        with the id member set to zero. Eventually, on XQueryTree()
        failure, X will generate a protocol error; that's why we don't
        bother throwing an exception.
    */
    virtual window parent() ;

    /**
        @brief  Get this window's children.
        @return Window's children.

        This function uses XQueryTree() to determine the list of child
        windows of the X window encapsulated by this object. If the call
        to XQueryTree() fails, this function will return an empty list
        and, eventually, X will generate a protocol error.
    */
    std::vector<window> children() ;

    /**
        @brief  Get screen number of this window.
        @return Window's screen number.

        This function returns the zero-based index of the physical
        screen this window is on. If Xinerama is active and this window
        overlaps two or more screens, this function will return the
        screen that has most of the window.

        @note This function calls XGetWindowAttributes(). If the call to
        XGetWindowAttributes() fails, this function will return -1 and,
        eventually, the X server will raise a protocol error.
    */
    virtual int screen() ;

    /**
        @brief An enumeration for the different event masks.

        This enumeration simply provides C++ names for the Xlib event
        masks that are exported to Python via @boostpylink's enum
        exporting facility.

................................................................................
    */
    void grab_key(const std::string& s) ;

    /**
        @brief  Show the window, i.e., map it.
        @return Nothing.
    */
    virtual void show() ;

    /**
        @brief  Hide this window, i.e., unmap it.
        @return Nothing.
    */
    virtual void hide() ;

    /**
        @brief  Check if window is currently mapped or not.
        @return True if window is mapped, false otherwise.

        This function uses XGetWindowAttributes() and checks the value
        of the XWindowAttributes's map_state field to see if the window
................................................................................
        IsViewable or IsUnviewable and false if either map_state is
        IsUnmapped or if the call to XGetWindowAttributes() fails.

        @note If XGetWindowAttributes() fails, we would expect a
        minxlib::protocol_error to be raised soon after. That's why this
        function returns false instead of throwing an exception.
    */
    virtual bool is_mapped() ;

    /**
        @brief  Move and resize the window.
        @param  x Window's x coordinate relative to parent's origin.
        @param  y Window's y coordinate relative to parent's origin.
        @param  w Window's width (not counting its border).
        @param  h Window's height (not counting its border).
        @return Nothing.

        This function sets the window geometry by calling
        XMoveResizeWindow(). Please consult Xlib documentation for
        further details.
    */
    virtual void move_resize(int x, int y, int w, int h) ;

    /**
        @brief  An enumeration for the different configure values.

        This enumeration simply provides C++ names for the Xlib value
        mask for configure requests. These names are exported to Python
        via @boostpylink's enum exporting facility.
................................................................................
        bitwise OR of the configure_mask enums.

        Please consult Xlib documentation for further details. For
        example, the following page may be instructive:

            http://tronche.com/gui/x/xlib/window/configure.html
    */
    virtual void configure(int x, int y, int w, int h, int b,
                           const window* s, int t, unsigned int v) ;

    /**
        @brief  Set window's border color and size.
        @param  c Three-byte RGB spec.
        @param  s Border size (in pixels).
        @return Nothing.

        This function calls XSetWindowBorderWidth() and
        XSetWindowBorder() to specify the window's border color and size
        to the desired values.
    */
    virtual void set_border_attr(unsigned long c, unsigned int s) ;

    /**
        @brief  Retrieve window's size, position, and border width.
        @return STL vector of ints containing window geometry.

        This function uses XGetGeometry() to determine the window's size,
        position, and border width. It returns an STL vector of integers
        containing the following five values:

            @li Element 0: x-coordinate of window's top-left corner
            @li Element 1: y-coordinate of window's top-left corner
            @li Element 2: window width
            @li Element 3: window height
            @li Element 4: window's border width


        If the call to XGetGeometry() fails, this function will return an
        empty vector.

        @note On the Python side, the STL vector returned by this
        function will be converted into a Python list. Also, since we
        would expect a minxlib::protocol_error to be raised soon after a

        failed XGetGeometry(), we return and empty list instead of
        throwing an exception in case the call to XGetGeometry() bombs.

    */
    virtual std::vector<int> geometry() ;

    /**
        @brief  Set input focus on this window and raise it.
        @return Nothing.
    */
    virtual void focus() ;

    /**
        @brief  Kill this window.
        @return Nothing.

        This method destroys the X window encapsulated by this object by
        killing the X client application that created the window. If the
        window supports the WM_DELETE_WINDOW protocol, this method will
        use that to kill the window and its client application.
        Otherwise, it will simply use the XKillClient() function to
        destroy the window and its client by brute force.
    */
    virtual void kill() ;

    /**
        @brief  Kill this window using brute force.
        @return Nothing.

        This method destroys the X window encapsulated by this object
        without trying a graceful shutdown via WM_DELETE_WINDOW. It is
        meant to be used on those clients whose windows advertise
        support for WM_DELETE_WINDOW but don't implement the delete
        protocol properly and stay alive without good cause despite a
        user-initiated kill request.
    */
    virtual void nuke() ;

    /**
        @brief  Destructor.
        @return Nothing.

        Clean-up for when a window object is deleted. Really, there's
        nothing to do because this class is only a thin wrapper around X
        windows and we only maintain a pointer to the Xlib display
        structure and an integral ID. We don't actually create or acquire
        anything when this class is instantiated; consequently, there's
        nothing to destroy or release.
    */
    virtual ~window() ;
} ; // class window

} // namespace minxlib

//----------------------------------------------------------------------

#endif // #ifndef MINXLIB_WINDOW_DOT_HH

Changes to wiki/design.wiki.

3
4
5
6
7
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
..
24
25
26
27
28
29
30
31
32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
58
59
..
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
108
109
110
111
112
113
114

















115
116
117
118
119
120
121
...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154







155
156
157
158
159
160
161
<h1>Design and Implementation Notes</h1>

The following diagram shows a high-level view of the different classes
and roughly how they're connected to each other.

<img src="img/object_diagram.png" width="900" height="337"/>

The <tt>minx.core</tt> classes are written in Python whereas the
<tt>minxlib</tt> classes are written in C++ and exposed to <b>Minx's</b>
Python core.

The organization of the code mirrors the above diagram. All the
<tt>minx.core</tt> classes are in the <em>minx/core</em> directory and

the <tt>minxlib</tt> classes are in the <em>minx/minxlib</em> directory.

<hr>

<h2>minx.core</h2>

<h3>minx.core.wm</h3>

................................................................................
This class is the main window manager object that end-users will create.
It maintains all the necessary state and provides the interface for
customizing the window manager.

When you create a <tt>wm</tt> object, you can supply a <tt>config</tt>
object to its constructor to customize various attributes such as the
border colors. To further customize Minx's responses to various events,
you can add your own hook functions to the <tt>wm.hooks</tt> object.
After these setup steps, call <tt>wm.start()</tt> to run the window
manager.


The <tt>start()</tt> method connects to the X server by creating a
<tt>minxlib::display</tt> object, performs some initialization so as to
receive various window management related events, and then enters the
event loop. The actual event handling is delegated to the
<tt>minx.core.xevents</tt> object, which has the necessary event handler
callbacks.

X errors are handled by <tt>minx.core.wm</tt> as part of its event loop.

<h3>minx.core.hooks</h3>

This class implements a mapping between strings and corresponding
function lists. The string keys identify different events. For example,
all X events are of the form "x_something" (x_map_request,
x_create_notify, etc.). Most end-user hooks are of the form "some_hook".

Key bindings are also handled as hooks (their names are not of the form
"some_hook"; they follow a different
[/doc/rel1.x/wiki/key-bindings.wiki|pattern]).

All objects within the system that have a reference to the <tt>wm</tt>
object can get at the hook map and add functions to it. This map is
meant to be used inside the window manager itself as well as by
end-users (for customizing Minx).

<h3>minx.core.xevents</h3>
................................................................................
constructor creates an <tt>xevents</tt> object, passing it a reference
to itself. Through this reference to <tt>wm</tt>, <tt>xevents</tt> is
able to add its handlers to <tt>wm.hooks</tt>.

Now, when <tt>wm</tt> gets events from the X server, it triggers the
appropriate handler in <tt>xevents</tt> via its map of hooks.

<h3>minx.core.window and minx.core.focus_list</h3>

The <tt>minx.core.window.window</tt> class is used to represent
top-level windows, which are created by <tt>xevents</tt> in response to
the "x_create_notify" event.


The <tt>minx.core.window</tt> module maintains an internal list to keep
track of all extant top-level window objects. When a top-level window is
destroyed, its object is removed from the above-mentioned internal list.

When a <tt>minx.core.window.window</tt> is created, <tt>xevents</tt>
passes it a reference to the <tt>wm</tt> object. Thus, all top-level
window objects have access to the window manager's <tt>hooks</tt>. On
creation, <tt>window</tt> installs some relevant X event handlers for
itself in the system <tt>hooks</tt>. Among other things, these hooks
are responsible for changing the window's border color on focus changes.

Not all top-level windows can be focused. For example, the GNOME and
XFCE terminal programs create a couple of hidden top-level windows.
Therefore, only a subset of the <tt>window</tt> objects maintained by
the <tt>minx.core.window</tt> module can actually receive input focus.
These windows are put in the <tt>focus_list</tt> by the <tt>xevents</tt>
object. This usually happens when the X server sends a
<tt>MapNotify</tt> event for a top-level window to Minx. When
<tt>minx.core.wm</tt> receives an <tt>UnmapNotify</tt> for a top-level
window, <tt>xevents</tt> will remove the corresponding <tt>window</tt>
from the <tt>focus_list</tt>.

<h3>minx.core.config</h3>

This class allows end-users to effect simple customizations such as
changing the window border colors and sizes. End-users will have to
instantiate this class and change its attributes to get the desired
customizations. The resulting <tt>config</tt> object should be passed to
................................................................................
the <tt>minx.core.wm</tt> constructor. Any object that has a reference
to the <tt>wm</tt> object can then get at its <tt>config</tt> instance
to access the different settings.

If end-users do not create a custom <tt>config</tt>, Minx will use
default values for all its supported customizations.


















<hr>

<h2>minxlib</h2>

minxlib is a small C++ library that provides a high-level,
object-oriented API to Minx's Python core for interfacing with Xlib. We
could have used [http://python-xlib.sourceforge.net/|python-xlib]
................................................................................
[http://wayland.freedesktop.org/|Wayland] whenever that becomes the
preferred means of talking to the video hardware.

Note that minxlib is not a thin wrapper around Xlib. It provides an API
that the rest of Minx actually needs. That is, the primary goal of
minxlib is not to wrap around all of Xlib but only that part of it that
we actually need for Minx. Moreover, minxlib provides a high-level API
that goes beyond the primitive one implemented by Xlib.  For example,
minxlib has a function to return a list of all the root windows across
all screens. Thus, the Python parts of Minx don't have to deal with the
low-level Xlib functions to enumerate the root windows one-by-one.

<h3>minxlib::display</h3>

The <tt>display</tt> class encapsulates the connection to the X server.
It provides the conduit for retrieving events from the server and for
converting X errors into exception objects that Minx's Python core can
handle.

<h3>minxlib::window</h3>

This class encapsulates an X window. Note that whereas
<tt>minx.core.window</tt> is for top-level windows,
<tt>minxlib::window</tt> represents any kind of X window. The
<tt>minxlib::window</tt> class takes care of the details of specifying X
event masks. It also provides various window-related operations such as
showing, hiding, resizing, moving, focusing, etc. 








<h3>minxlib::event and minxlib::exception</h3>

These two classes provide an object-oriented interface to X events and
errors.  Different types of events are converted into instances of
subclasses of <tt>minxlib::event</tt>. Similarly, different kinds of
errors are converted into instances of classes derived from







|
|
|


|
>
|







 







|
|
|

>
|
|











|
|
>
|
<
|







 







|

|
|
|
>

<
|
<

<
<
<
<
<
<
<
|
|
<
|

|
|
|
|
|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|











|

|
|
<
<
|
|
>
>
>
>
>
>
>







3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
..
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83

84







85
86

87
88
89
90
91
92
93
94
95
96
97
98
99
100
...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<h1>Design and Implementation Notes</h1>

The following diagram shows a high-level view of the different classes
and roughly how they're connected to each other.

<img src="img/object_diagram.png" width="900" height="337"/>

The <tt>minx.core</tt> and <tt>minx.layout</tt> classes are written in
Python whereas the <tt>minxlib</tt> classes are written in C++ and
exposed to Minx's Python core.

The organization of the code mirrors the above diagram. All the
<tt>minx.core</tt> classes are in the <em>minx/core</em> directory, the
<tt>minx.layout</tt> classes in <em>minx/layout</em>, and the
<tt>minxlib</tt> classes are in the <em>minx/minxlib</em> directory.

<hr>

<h2>minx.core</h2>

<h3>minx.core.wm</h3>

................................................................................
This class is the main window manager object that end-users will create.
It maintains all the necessary state and provides the interface for
customizing the window manager.

When you create a <tt>wm</tt> object, you can supply a <tt>config</tt>
object to its constructor to customize various attributes such as the
border colors. To further customize Minx's responses to various events,
you can add your own hook functions to the <tt>wm.hooks</tt> object. For
layouts, you can deal with the <tt>wm.layouts</tt> object, which is an
instance of the <tt>minx.core.layman</tt> class.

After these setup steps, call <tt>wm.start()</tt> to run the window
manager. The <tt>start()</tt> method connects to the X server by creating
a <tt>minxlib::display</tt> object, performs some initialization so as to
receive various window management related events, and then enters the
event loop. The actual event handling is delegated to the
<tt>minx.core.xevents</tt> object, which has the necessary event handler
callbacks.

X errors are handled by <tt>minx.core.wm</tt> as part of its event loop.

<h3>minx.core.hooks</h3>

This class implements a mapping between strings and corresponding
function lists. The string keys identify different events. For example,
all X events are of the form "<tt>x_something</tt>"
(<tt>x_map_request</tt>, <tt>x_create_notify</tt>, etc.). Most end-user
hooks are of the form "<tt>some_hook</tt>". Key bindings are also handled
as hooks (their names are not of the form "<tt>some_hook</tt>"; they

follow a different [/doc/ckout/wiki/key-bindings.wiki|pattern]).

All objects within the system that have a reference to the <tt>wm</tt>
object can get at the hook map and add functions to it. This map is
meant to be used inside the window manager itself as well as by
end-users (for customizing Minx).

<h3>minx.core.xevents</h3>
................................................................................
constructor creates an <tt>xevents</tt> object, passing it a reference
to itself. Through this reference to <tt>wm</tt>, <tt>xevents</tt> is
able to add its handlers to <tt>wm.hooks</tt>.

Now, when <tt>wm</tt> gets events from the X server, it triggers the
appropriate handler in <tt>xevents</tt> via its map of hooks.

<h3>minx.core.layman</h3>

This class implements a layout manager, which keeps track of the
different layouts. The <tt>layman</tt> class maintains a list of layout
objects and provides an API for adding layouts, looking for them, and so
on.


<h3>minx.core.focus_list</h3>









Not all top-level windows can be focused. For example, the GNOME and XFCE
terminal programs create a couple of hidden top-level windows. Therefore,

only a subset of the top-level windows can actually receive input focus.
These windows are put in the <tt>focus_list</tt> by the <tt>xevents</tt>
object. This usually happens when the X server sends a <tt>MapNotify</tt>
event for a top-level window to Minx. When <tt>minx.core.wm</tt> receives
an <tt>UnmapNotify</tt> for a top-level window, <tt>xevents</tt> will
remove the corresponding <tt>minxlib::window</tt> from the
<tt>focus_list</tt>.

<h3>minx.core.config</h3>

This class allows end-users to effect simple customizations such as
changing the window border colors and sizes. End-users will have to
instantiate this class and change its attributes to get the desired
customizations. The resulting <tt>config</tt> object should be passed to
................................................................................
the <tt>minx.core.wm</tt> constructor. Any object that has a reference
to the <tt>wm</tt> object can then get at its <tt>config</tt> instance
to access the different settings.

If end-users do not create a custom <tt>config</tt>, Minx will use
default values for all its supported customizations.

<hr>

<h2>minx.layout</h2>

<h3>minx.layout.base</h3>

This is a base class for all Minx layouts. For the most part, it contains
empty implementations for most events and other functions that are meant
to be overridden by subclasses. Additionally, the
<tt>minx.layout.base</tt> module contains a few event handlers for
various bits of layout related functionality.

<h3>minx.layout.full</h3>

This layout implements a policy of showing one window at a time, resizing
all the windows it manages to occupy the entire area available to it.

<hr>

<h2>minxlib</h2>

minxlib is a small C++ library that provides a high-level,
object-oriented API to Minx's Python core for interfacing with Xlib. We
could have used [http://python-xlib.sourceforge.net/|python-xlib]
................................................................................
[http://wayland.freedesktop.org/|Wayland] whenever that becomes the
preferred means of talking to the video hardware.

Note that minxlib is not a thin wrapper around Xlib. It provides an API
that the rest of Minx actually needs. That is, the primary goal of
minxlib is not to wrap around all of Xlib but only that part of it that
we actually need for Minx. Moreover, minxlib provides a high-level API
that goes beyond the primitive one implemented by Xlib. For example,
minxlib has a function to return a list of all the root windows across
all screens. Thus, the Python parts of Minx don't have to deal with the
low-level Xlib functions to enumerate the root windows one-by-one.

<h3>minxlib::display</h3>

The <tt>display</tt> class encapsulates the connection to the X server.
It provides the conduit for retrieving events from the server and for
converting X errors into exception objects that Minx's Python core can
handle.

<h3>minxlib::window and minxlib::root_window</h3>

This class encapsulates an X window, taking care of the details of
specifying X event masks and setting up key bindings. It also provides


various window-related operations such as showing, hiding, resizing,
moving, focusing, etc.

The <tt>root_window</tt> class is derived from <tt>minxlib::window</tt>
and provides an encapsulation of X's root windows, taking into account
support for Xinerama. Minx provides one <tt>root_window</tt> for each
physical screen. If Xinerama is active, there'll only be one logical
screen; however, Minx will return multiple <tt>root_window</tt> objects,
each with the same X ID but different geometries.

<h3>minxlib::event and minxlib::exception</h3>

These two classes provide an object-oriented interface to X events and
errors.  Different types of events are converted into instances of
subclasses of <tt>minxlib::event</tt>. Similarly, different kinds of
errors are converted into instances of classes derived from

Changes to wiki/faq.wiki.

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
...
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
...
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
<hr>

<a name="how-launch-apps"></a>
<h3>How can I launch applications using custom key bindings?</h3>

The [/doc/rel1.x/api/classminx_1_1core_1_1wm_1_1wm.html|wm] class has a
<tt>spawn()</tt> function that you can use for this purpose. In fact,
there is a 
[/doc/rel1.x/wiki/hooks-list.wiki#launch_terminal|default key binding]
that calls <tt>wm.spawn()</tt> to launch a terminal window. In a similar
vein, you could define keys as shown below:

<verbatim>
    #!/usr/bin/env python

................................................................................
<verbatim>
   #!/usr/bin/env python

   import minx

   try:
      print('Minx version string: {}'.format(minx.version))
      print('Minx version: major = {}, minor = {}, patch = {}'.  
            format(minx.version.major, minx.version.minor, minx.version.patch))
   except AttributeError: # no version attribute in minx module
      pass # probably using development version of Minx
</verbatim>

Note that <tt>minx.version</tt> is not available in development versions
of Minx. Only officially released versions of Minx have a version number
................................................................................
<h3>Why are you using Doxygen to document the Minx API? Since Minx is
essentially a Python library, wouldn't it be better to use Sphinx, which
not only rhymes with Minx, but also seems to be a de facto standard now
for Python projects?</h3>

I did consider [http://sphinx-doc.org/|Sphinx]. In fact, originally, I
wanted to put the Minx API documentation on
[http://readthedocs.org/|ReadTheDocs].  However, I balked once I
realized that getting Sphinx to automatically generate the documentation
for [#what-is-minxlib|minxlib], the C++ part of Minx, would be
[http://michaeljones.github.com/breathe/|a hassle].

Setting up a bridge between [http://sphinx-doc.org/|Sphinx] and
[http://www.doxygen.org/|Doxygen] seems like too much work without
enough of a payoff especially because:

  *  Doxygen integrates well with [http://www.cmake.org/|CMake], which







|







 







|







 







|
|
|







297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
...
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
...
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
<hr>

<a name="how-launch-apps"></a>
<h3>How can I launch applications using custom key bindings?</h3>

The [/doc/rel1.x/api/classminx_1_1core_1_1wm_1_1wm.html|wm] class has a
<tt>spawn()</tt> function that you can use for this purpose. In fact,
there is a
[/doc/rel1.x/wiki/hooks-list.wiki#launch_terminal|default key binding]
that calls <tt>wm.spawn()</tt> to launch a terminal window. In a similar
vein, you could define keys as shown below:

<verbatim>
    #!/usr/bin/env python

................................................................................
<verbatim>
   #!/usr/bin/env python

   import minx

   try:
      print('Minx version string: {}'.format(minx.version))
      print('Minx version: major = {}, minor = {}, patch = {}'.
            format(minx.version.major, minx.version.minor, minx.version.patch))
   except AttributeError: # no version attribute in minx module
      pass # probably using development version of Minx
</verbatim>

Note that <tt>minx.version</tt> is not available in development versions
of Minx. Only officially released versions of Minx have a version number
................................................................................
<h3>Why are you using Doxygen to document the Minx API? Since Minx is
essentially a Python library, wouldn't it be better to use Sphinx, which
not only rhymes with Minx, but also seems to be a de facto standard now
for Python projects?</h3>

I did consider [http://sphinx-doc.org/|Sphinx]. In fact, originally, I
wanted to put the Minx API documentation on
[http://readthedocs.org/|ReadTheDocs]. However, I balked once I realized
that getting Sphinx to automatically generate the documentation for
[#what-is-minxlib|minxlib], the C++ part of Minx, would be
[http://michaeljones.github.com/breathe/|a hassle].

Setting up a bridge between [http://sphinx-doc.org/|Sphinx] and
[http://www.doxygen.org/|Doxygen] seems like too much work without
enough of a payoff especially because:

  *  Doxygen integrates well with [http://www.cmake.org/|CMake], which

Changes to wiki/home.wiki.

1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
<title>Home</title>

<h1>About Minx</h1>

Minx is a tiling X window manager. Here are some of its main features and design
goals:

  *  <b>Minimalism:</b> Minx does not put any decorations on or around top-level
     windows except for a simple border. It also does not support task bars,
     panels, icons, menus, etc. All it does is implement window layout policies

     that can be tailored to meet personal tastes.

  *  <b>Customizability:</b> Minx provides hooks into each and every aspect of
     its operation so that end-users can alter its behaviour to match their
     exact workflow.

  *  <b>Extensibility:</b> Functionality not in the Minx core can be added with
     minimal effort. For example, the bookmarks feature (aka workspaces),
     although part of the core distribution, is, in fact, implemented by taking
     advantage of Minx's ability to add arbitrary state to the window manager
     object. In a similar vein, new layouts are quite straightforward to
     implement.

  *  <b>Ease of use:</b> The default configuration (hopefully) provides a
     reasonable out-of-the-box experience (albeit different from full-blown
     desktop environments such as GNOME or KDE and, of course, subject to the
     constraints imposed by the desire to remain minimal). However, changing the
     defaults is easy.

  *  <b>Keyboard centrism:</b> All of Minx's actions can be accessed from the
     keyboard, including window resizing and moving with as few as 4-5
     keystrokes. The mouse, however, is supported.

  *  <b>Documentation:</b> Minx has no [#documentation|dark corners]. For users,
     there are plenty of how-to's explaining common as well as not-so-common
     configuration tasks. For developers, the code contains comments explaining
     intent, rationale, etc. There are also Wiki pages providing design
     overviews, explaining how the code is structured, why it is that way, and,

     also, the development methodology (branching policy, etc.).

Enjoy.

<h1>News</h1>

<h3>April 14, 2013</h3>
  *  <em>Version 0.1.9 blasts off.</em><br/>




|
|

|
|
|
>
|

|
|
|

|
|
|
|
|
|


|
|
|
|

|
|
|

|
|
|
|
|
>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<title>Home</title>

<h1>About Minx</h1>

Minx is a tiling X window manager. Here are some of its main features and
design goals:

  *  <b>Minimalism:</b> Minx does not put any decorations on or around
     top-level windows except for a simple border. It also does not
     support task bars, panels, icons, menus, etc. All it does is
     implement window layout policies that can be tailored to meet
     personal tastes.

  *  <b>Customizability:</b> Minx provides hooks into each and every
     aspect of its operation so that end-users can alter its behaviour to
     match their exact workflow.

  *  <b>Extensibility:</b> Functionality not in the Minx core can be
     added with minimal effort. For example, the bookmarks feature (aka
     workspaces), although part of the core distribution, is, in fact,
     implemented by taking advantage of Minx's ability to add arbitrary
     state to the window manager object. In a similar vein, new layouts
     are quite straightforward to implement.

  *  <b>Ease of use:</b> The default configuration (hopefully) provides a
     reasonable out-of-the-box experience (albeit different from
     full-blown desktop environments such as GNOME or KDE and, of course,
     subject to the constraints imposed by the desire to remain minimal).
     However, changing the defaults is easy.

  *  <b>Keyboard centrism:</b> All of Minx's actions can be accessed from
     the keyboard, including window resizing and moving with as few as
     4-5 keystrokes. The mouse, however, is supported.

  *  <b>Documentation:</b> Minx has no [#documentation|dark corners]. For
     users, there are plenty of how-to's explaining common as well as
     not-so-common configuration tasks. For developers, the code contains
     comments explaining intent, rationale, etc. There are also Wiki
     pages providing design overviews, explaining how the code is
     structured, why it is that way, and, also, the development
     methodology (branching policy, etc.).

Enjoy.

<h1>News</h1>

<h3>April 14, 2013</h3>
  *  <em>Version 0.1.9 blasts off.</em><br/>

Changes to wiki/hooks-list.wiki.

5
6
7
8
9
10
11

12

13
14
15
16
17
18
19

20
21
22
23
24
25
26
..
32
33
34
35
36
37
38
























































39
40
41
42
43
44
45
...
102
103
104
105
106
107
108








































109
110
111
112
113
114
115
...
170
171
172
173
174
175
176







177
178
179
180
181
182
183
end-users to define their own hooks and triggers those hooks in response
to different events/situations.

This page documents all of Minx's currently supported hooks, both
internal ones and those meant for end-users. Here is the complete list:

[#end-user-hooks|End-user Hooks]

  #  [#manage_hook|manage_hook]


[#default-key-bindings|Default Key Bindings]
  #  [#focus_next|A-Tab]
  #  [#focus_prev|S-A-Tab]
  #  [#focus_kill|A-F4]
  #  [#focus_nuke|C-F4]
  #  [#launch_terminal|C-A-T]


[#internal-hook|Internal Hooks]
  #  [#x_create_notify|x_create_notify]
  #  [#x_destroy_notify|x_destroy_notify]
  #  [#x_configure_request|x_configure_request]
  #  [#x_map_request|x_map_request]
  #  [#x_map_notify|x_map_notify]
................................................................................

<a name="end-user-hooks"></a>
<h1>End-user Hooks</h1>

Minx triggers these hooks to allow end-users to customize how the window
manager responds to different events.

























































<a name="manage_hook"></a>
<h2>manage_hook</h2>

Minx triggers this hook when a new window is created. It will pass to
each <tt>manage_hook</tt> a
[/doc/rel1.x/api/classminxlib_1_1window.html|minxlib.window] object and
expects a <tt>True</tt> or <tt>False</tt> return value to decide whether
................................................................................
</verbatim>

In a similar vein, you can use regular expressions and other such
techniques to check against a multitude of patterns to decide whether or
not to manage a given window without needing more than one
<tt>manage_hook</tt>.









































<hr>

<a name="default-key-bindings"></a>
<h1>Default Key Bindings</h1>

Key bindings are simply hooks whose names follow the pattern described
[/doc/rel1.x/wiki/key-bindings.wiki|here]. Whenever a keyboard event
................................................................................
    conf = minx.core.config()
    conf.terminal = 'gnome-terminal'

    wm = minx.core.wm(conf)
    wm.start()
</verbatim>








<hr>

<a name="internal-hooks"></a>
<h1>Internal Hooks</h1>

Minx defines and uses these hooks internally to respond to events
generated by the X server. In general, you should not override these







>

>







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
end-users to define their own hooks and triggers those hooks in response
to different events/situations.

This page documents all of Minx's currently supported hooks, both
internal ones and those meant for end-users. Here is the complete list:

[#end-user-hooks|End-user Hooks]
  #  [#init_hook|init_hook]
  #  [#manage_hook|manage_hook]
  #  [#receptive_layout_hook|receptive_layout_hook]

[#default-key-bindings|Default Key Bindings]
  #  [#focus_next|A-Tab]
  #  [#focus_prev|S-A-Tab]
  #  [#focus_kill|A-F4]
  #  [#focus_nuke|C-F4]
  #  [#launch_terminal|C-A-T]
  #  [#quit_wm|C-A-X]

[#internal-hook|Internal Hooks]
  #  [#x_create_notify|x_create_notify]
  #  [#x_destroy_notify|x_destroy_notify]
  #  [#x_configure_request|x_configure_request]
  #  [#x_map_request|x_map_request]
  #  [#x_map_notify|x_map_notify]
................................................................................

<a name="end-user-hooks"></a>
<h1>End-user Hooks</h1>

Minx triggers these hooks to allow end-users to customize how the window
manager responds to different events.

<a name="init_hook"></a>
<h2>init_hook</h2>

When you start Minx (by calling the
[/doc/ckout/api/classminx_1_1core_1_1wm_1_1wm.html|wm] object's start
method), it goes through the following sequence of steps:

  #  Connect to the X server
  #  Configure X event masks and key bindings
  #  Trigger the init hook
  #  Manage existing windows
  #  Initiate the X event loop

The intent of the init hook is to allow you to insert some custom
initialization before Minx starts managing windows and processing events
but after it has obtained a valid X connection. When the init hook is
called, you can be sure that the <tt>wm</tt> object's <tt>display</tt>
and <tt>root_windows</tt> attributes are available to you. The
<tt>display</tt> attribute is an instance of the
[/doc/ckout/api/classminxlib_1_1display.html|<tt>minxlib::display</tt>]
class and isn't something you would usually need to deal with directly.
However, <tt>wm.root_windows</tt> is almost certainly an attribute of
interest: it contains a list of
[/doc/ckout/api/classminxlib_1_1root__window.html|<tt>minxlib::root_window</tt>]
objects, one for each physical screen you have.

Typically, in the init hook, you will create layouts for each screen as
shown below:

<verbatim>
    #!/usr/bin/env python

    import minx

    def my_init_hook(m):
        scr = m.root_windows
        m.layouts.add(minx.layout.tall(m, scr[0]))
        if len(scr) > 1:
           for w in scr[1:]:
               m.layouts.add(minx.layout.full(m, w))

    wm = minx.core.wm()
    wm.hooks.add('init_hook', my_init_hook)
    wm.start()
</verbatim>

The above example sets up the first screen to use the tall layout and the
full layout on the remaining screens. Note that the tall layout has not
yet been implemented; it is used above merely for illustration.

There is no per-screen restriction on the number of layouts nor any
requirement that a layout occupy the entire screen. We kept it simple for
the sake of the example. However, you can create multiple layouts per
screen, assign them to different parts of the screens, and use whatever
criteria that fit your needs and that you can code up.

<a name="manage_hook"></a>
<h2>manage_hook</h2>

Minx triggers this hook when a new window is created. It will pass to
each <tt>manage_hook</tt> a
[/doc/rel1.x/api/classminxlib_1_1window.html|minxlib.window] object and
expects a <tt>True</tt> or <tt>False</tt> return value to decide whether
................................................................................
</verbatim>

In a similar vein, you can use regular expressions and other such
techniques to check against a multitude of patterns to decide whether or
not to manage a given window without needing more than one
<tt>manage_hook</tt>.

<a name="receptive_layout_hook"></a>
<h2>receptive_layout_hook</h2>

Once a window gets past the [#manage_hook|manage hook], to manage it,
Minx will assign it to a layout. The receptive layout hook, allows you to
find or create a layout for each new window. Here is an example:

<verbatim>
    #!/usr/bin/env python

    import minx

    def my_receptive_layout_hook(w):
        prop = w.properties()
        if 'gimp' in prop['class'].lower():
            global wm
            try:
                L = wm.layouts.find('gimp')
                return L
            except minx.core.layman.unknown_layout:
                parent = wm.root_windows[w.screen()]
                return minx.layout.gimp(wm, parent)
        return None

    wm = minx.core.wm()
    wm.hooks.add('receptive_layout_hook', my_receptive_layout_hook)
    wm.start()
</verbatim>

The above example returns a new gimp layout for the first Gimp window and
reuses that layout for subsequent windows that Gimp creates. For all
other window types, it returns nothing, which makes Minx use its default
policies for layout selection (read all about that on the
[/doc/ckout/api/classminx_1_1core_1_1layman_1_1layman.html|layout
manager's API page]).

Please note that there is no gimp layout. Thus, the above code won't
actually work; it is only meant to illustrate how the receptive layout
hook is meant to be used.

<hr>

<a name="default-key-bindings"></a>
<h1>Default Key Bindings</h1>

Key bindings are simply hooks whose names follow the pattern described
[/doc/rel1.x/wiki/key-bindings.wiki|here]. Whenever a keyboard event
................................................................................
    conf = minx.core.config()
    conf.terminal = 'gnome-terminal'

    wm = minx.core.wm(conf)
    wm.start()
</verbatim>

<a name="quit_wm"></a>
<h2>C-A-X</h2>

This key binding makes Minx get out of its event loop. Usually, this will
also quit the window manager (unless you have code in your Minx init file
after the call to <tt>wm.start()</tt>).

<hr>

<a name="internal-hooks"></a>
<h1>Internal Hooks</h1>

Minx defines and uses these hooks internally to respond to events
generated by the X server. In general, you should not override these

Changes to wiki/img/Makefile.

23
24
25
26
27
28
29
30
31
32
33
34
35



36
37
38
39
40
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Minx. If not, see <http://www.gnu.org/licenses/>.
#

# Rule for converting .dot files to .png
# NOTE: GraphViz's dot program should be in PATH.
.SUFFIXES: .dot .png
.dot.png:
	dot $< -Tpng -o $@




# List of images to be built
TARGETS = object_diagram.png

# Main rule for building all the targets
all: $(TARGETS)







|
|
|



>
>
>

|



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with Minx. If not, see <http://www.gnu.org/licenses/>.
#

# Rule for converting .dot and .asy files to .png
# NOTE: GraphViz's dot program and the Asymptote compiler should be in PATH.
.SUFFIXES: .asy .dot .png
.dot.png:
	dot $< -Tpng -o $@

.asy.png:
	asy -f png -o $@ $<

# List of images to be built
TARGETS = logo.png object_diagram.png

# Main rule for building all the targets
all: $(TARGETS)

Added wiki/img/logo.asy.



















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//
// @file  logo.asy
// @brief Draw Minx logo.
//
// This short Asymptote program draws the Minx logo, which, in ASCII
// art, looks as shown below:
//
//      +-----+---+
//      |  M  |   |
//      +-+---+ X |
//      |i| N |   |
//      +-+---+---+
//
// The idea is to show that Minx is a tiling window manager. Each
// rectangle in the logo represents a tiled window. One of the tiles
// will be shown with an emphasized border (i.e., different color) to
// indicate that it has the input focus.
//

//
// Copyright (C) 2012 The Minx Project Developers
//
// See wiki/copyright.wiki in the top-level directory of the Minx
// source distribution for the full list of authors who have
// contributed to this project.
//

//
// This file is part of Minx.
//
// Minx is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Minx is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Minx. If not, see <http://www.gnu.org/licenses/>.
//

//------------------------------ MAIN ----------------------------------

// Init
real phi = (1 + sqrt(5))/2 ; // Golden Ratio
real logo_width  = 1.25 * phi ;
real logo_height = logo_width/phi ;

defaultpen(2) ; // draw thick window/tile borders

// NOTE: Use picture size slightly larger than above width and height
// extents. Otherwise, top and right edges of the logo don't show up.
size(logo_width * 1.01cm, logo_height * 1.01cm) ;

// The outer box (see ASCII art in file's header comment)
pair outer_bl = (0,0) ;
pair outer_br = (logo_width,0) ;
pair outer_tr = (logo_width, logo_height) ;
pair outer_tl = (0, logo_height) ;
draw(outer_bl--outer_br--outer_tr--outer_tl--cycle) ;

// Vertical partition between M and X
pair vert_mx_b = outer_bl + (outer_br - outer_bl) * phi/(phi + 1) ;
pair vert_mx_t = (vert_mx_b.x, logo_height) ;
draw(vert_mx_b--vert_mx_t) ;

// Horizontal partition between M and i and N
pair horz_min_l = outer_bl + (outer_tl - outer_bl)/(phi + 1) ;
pair horz_min_r = (vert_mx_b.x, horz_min_l.y) ;
draw(horz_min_l--horz_min_r) ;

// Vertical partition between i and N
pair vert_in_b = outer_bl + (vert_mx_b - outer_bl)/(phi + 1) ;
pair vert_in_t = (vert_in_b.x, horz_min_l.y) ;
draw(vert_in_b--vert_in_t) ;

// The labels within the above boxes
label("\textbf{\LARGE M}",
      ((outer_bl.x  + vert_mx_b.x)/2, (horz_min_l.y +   outer_tl.y)/2)) ;
label("\textbf{i}",
      ((outer_bl.x  + vert_in_b.x)/2, (  outer_bl.y + horz_min_l.y)/2)) ;
label("\textbf{N}",
      ((vert_in_b.x + vert_mx_b.x)/2, (  outer_bl.y + horz_min_l.y)/2)) ;
label("\textbf{\Large X}",
      ((vert_mx_b.x +  outer_br.x)/2, (  outer_br.y +   outer_tr.y)/2)) ;

// Finally, highlight one of the tiles to indicate that it has the input
// focus...
//draw(horz_min_l--horz_min_r--vert_mx_t--outer_tl--cycle, red) ; // M tile
draw(vert_mx_b--outer_br--outer_tr--vert_mx_t--cycle, red) ; // X tile

//----------------------------------------------------------------------

////////////////////////////////////////////////
// Editor config:                             //
////////////////////////////////////////////////
// Local Variables:                           //
// indent-tabs-mode: nil                      //
// End:                                       //
////////////////////////////////////////////////
// vim: set expandtab shiftwidth=4 tabstop=4: //
////////////////////////////////////////////////

Added wiki/img/logo.png.

cannot compute difference between binary files

Changes to wiki/img/object_diagram.dot.

29
30
31
32
33
34
35
36
37
38
39
40

41




42
43
44

45
46
47
48
49
50




51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66





67
68
69
70
71
72
73
*/

//----------------------------- DIAGRAM --------------------------------

graph minx_main {
    // minx.core objects
    wm          [label = "minx.core.wm"         ]
    window      [label = "minx.core.window"     ]
    focus_list  [label = "minx.core.focus_list" ]
    xevents     [label = "minx.core.xevents"    ]
    hooks       [label = "minx.core.hooks"      ]
    config      [label = "minx.core.config"     ]






    // minxlib objects
    minxlib_display     [label = "minxlib::display"  ]
    minxlib_window      [label = "minxlib::window"   ]

    minxlib_event       [label = "minxlib::event"    ]
    minxlib_keymap      [label = "minxlib::keymap"   ]
    minxlib_exception   [label = "minxlib::exception"]
    minxlib_logging     [label = "minxlib::logging"  ]

    // Connections between minx.core objects and also to minxlib objects




    wm -- hooks
    wm -- config
    wm -- focus_list -- window -- minxlib_window
    wm -- xevents -- window
    wm -- window
    wm -- minxlib_display

    // Internal connections inside minxlib
    minxlib_display   -- minxlib_event
    minxlib_display   -- minxlib_window
    minxlib_display   -- minxlib_exception
    minxlib_window    -- minxlib_keymap -- minxlib_event
    minxlib_display   -- minxlib_logging
    minxlib_window    -- minxlib_logging
    minxlib_event     -- minxlib_logging
    minxlib_exception -- minxlib_logging





}

//----------------------------------------------------------------------

/**********************************************/
/* Editor config:                             */
/**********************************************/







<




>

>
>
>
>

|
|
>
|
|
|
|

|
>
>
>
>


<
<
|
<


<
<
<
<
<
<
<
|
>
>
>
>
>







29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61


62

63
64







65
66
67
68
69
70
71
72
73
74
75
76
77
*/

//----------------------------- DIAGRAM --------------------------------

graph minx_main {
    // minx.core objects
    wm          [label = "minx.core.wm"         ]

    focus_list  [label = "minx.core.focus_list" ]
    xevents     [label = "minx.core.xevents"    ]
    hooks       [label = "minx.core.hooks"      ]
    config      [label = "minx.core.config"     ]
    layman      [label = "minx.core.layman"     ]

    // minx.layout objects
    base [label = "minx.layout.base"]
    full [label = "minx.layout.full"]

    // minxlib objects
    display     [label = "minxlib::display"    ]
    window      [label = "minxlib::window"     ]
    rwindow     [label = "minxlib::root_window"]
    event       [label = "minxlib::event"      ]
    keymap      [label = "minxlib::keymap"     ]
    exception   [label = "minxlib::exception"  ]
    logging     [label = "minxlib::logging"    ]

    // Connections between minx.core, minx.layout, and minxlib objects
    wm -- layman -- base -- full
    wm -- display --event
    wm -- xevents -- base -- window
    wm -- focus_list -- window
    wm -- hooks
    wm -- config


    wm -- rwindow


    // Internal connections inside minxlib







    display -- window -- rwindow -- logging
    display -- exception
    window  -- keymap -- event -- logging
    display -- logging
    window  -- logging
    keymap  -- logging
}

//----------------------------------------------------------------------

/**********************************************/
/* Editor config:                             */
/**********************************************/

Changes to wiki/img/object_diagram.png.

cannot compute difference between binary files

Changes to wiki/milestones.wiki.

53
54
55
56
57
58
59
60

61
62
63
64



65











66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
     manage them.
  *  <b>Version 0.1.5</b>: Add support for ignoring windows based on their
     properties (class, name, etc.).
  *  <b>Version 0.1.6</b>: Allow users to customize key bindings.
  *  <b>Version 0.1.7</b>: Add function for killing windows.
  *  <b>Version 0.1.8</b>: Add function for launching new applications.

<h1>Remaining Milestones</h1>


  *  <b>Version 0.2.0</b>: The focused window should fill the entire screen.
  *  <b>Version 0.2.1</b>: Layout engine infrastructure plus the "fundamental"
     layout, viz., <tt>minx.layout.matrix</tt>.



  *  <b>Version 0.2.2</b>: Resizing tiled windows with the keyboard.











  *  <b>Version 0.3.0</b>: Bookmarks (aka workspaces) via custom user state.
  *  <b>Version 0.4.0</b>: Floating windows (with support for size hints).
  *  <b>Version 0.4.1</b>: <tt>gimp</tt> layout.
  *  <b>Version 0.5.0</b>: Mouse support, i.e., window moving and resizing;
     also, focus follows mouse.
  *  <b>Version 0.6.0</b>: Defaults (so out-of-box user experience with minimal
     config is sane).
  *  <b>Version 0.7.0</b>: Packaging for various systems (FreeBSD,
     Debian-based).
  *  <b>Version 0.8.0</b>: Documentation for users and hackers.
  *  <b>Version 0.9.0</b>: Session management (if feasible or to the extent
     feasible).
  *  <b>Version 1.0.0</b>: Various fixes/tweaks (e.g., equivalent of a --replace
     option, sending window manager commands over local socket, helper apps such
     as window switcher and resizer) and release into the wild as "ready for
     prime-time."







|
>

|
<
|
>
>
>

>
>
>
>
>
>
>
>
>
>
>
|
|
<
<
<
|

|
|
|
|





53
54
55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81



82
83
84
85
86
87
88
89
90
91
92
     manage them.
  *  <b>Version 0.1.5</b>: Add support for ignoring windows based on their
     properties (class, name, etc.).
  *  <b>Version 0.1.6</b>: Allow users to customize key bindings.
  *  <b>Version 0.1.7</b>: Add function for killing windows.
  *  <b>Version 0.1.8</b>: Add function for launching new applications.

<a name="v02x"></a>
<h1>Version 0.2.x: Layouts and Related Functionality</h1>

  *  <b>Version 0.2.0</b>: The focused window should fill the entire

     screen, i.e., the full layout.
  *  <b>Version 0.2.1</b>: The manual layout, i.e., laying out windows by
     splitting horizontally or vertically (a la Emacs C-x 2 or C-x 3
     respectively).
  *  <b>Version 0.2.2</b>: Resizing tiled windows with the keyboard.
  *  <b>Version 0.2.3</b>: Laying out windows in rows and columns.
  *  <b>Version 0.2.4</b>: An implementation of XMonad's tall layout.
  *  <b>Version 0.2.5</b>: Laying out windows in a grid.
  *  <b>Version 0.2.6</b>: Support for moving windows between layouts.
  *  <b>Version 0.2.7</b>: Support for moving and resizing whole layouts.
  *  <b>Version 0.2.8</b>: Moving and resizing windows and layouts with
     the mouse.
  *  <b>Version 0.2.9</b>: A specialized layout for The GIMP.

<h1>Remaining Milestones</h1>

  *  <b>Version 0.3.0</b>: Snapshots (aka workspaces) via custom user state.
  *  <b>Version 0.4.0</b>: Focus policy infrastructure.



  *  <b>Version 0.5.0</b>: Defaults (so out-of-box user experience with minimal
     config is sane).
  *  <b>Version 0.6.0</b>: Packaging for various systems (FreeBSD,
     Debian-based, Arch).
  *  <b>Version 0.7.0</b>: Documentation for users and hackers.
  *  <b>Version 0.8.0</b>: Session management (if feasible or to the extent
     feasible).
  *  <b>Version 1.0.0</b>: Various fixes/tweaks (e.g., equivalent of a --replace
     option, sending window manager commands over local socket, helper apps such
     as window switcher and resizer) and release into the wild as "ready for
     prime-time."

Changes to wiki/process.wiki.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16

17
18
19
20
21

22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56

57
58
<title>Development Process</title>

<h1>About this Page</h1>

Minx is not a large project. In all likelihood, I will be the only developer and
its only user (well, there may be at most a handful of others who might use it).
Given that, the project is not well-served by a heavy-duty process and nor does

it have one. However, there is some method to the madness...

<hr>

<h1>Development and Branching Policy</h1>

<b>dev1.x</b> is the main development branch. Usually, development will proceed
linearly on this branch. However, if there are others contributing to the
project and/or there is a major feature that will continue for a while, we will

create feature branches off off <b>dev1.x</b>. When the feature is done, the
branch will be merged onto <b>dev1.x</b> and then, usually, closed.

<b>rel1.x</b> is the release branch. When we are ready to make a release, we
will merge from <b>dev1.x</b> and then tag (after tweaking as required).


<h2>Release-only Artifacts</h2>

There are certain things that live only on the release branch. For example, the
version numbering API, the changelog, release notes, and so on. These things are
never merged into the development branches. Consequently, no development ever
occurs on the <b>rel1.x</b> branch. We only merge to <b>rel1.x</b>, never from
it to <b>dev1.x</b> or some other branch.


<h3>Hotfixes</h3>

If we find a bug on <b>rel1.x</b> that needs an urgent fix, we will branch off
off <b>dev1.x</b> at the point where that particular release was made, apply the
fix on this "hotfix" branch and then merge the hotfix branch to both
<b>dev1.x</b> and <b>rel1.x</b> (tagging the release by increasing the version
number's patch level).

Although this approach is a little more complicated and results in a slightly
messier revision history, it allows us to maintain certain release-only items
(changelog, version numbering API, etc.) solely on <b>rel1.x</b> without
accidentally getting them onto a development branch (where they do not belong).


<hr>

<h1>Making a Release</h1>

  #  Merge from <b>dev1.x</b> to <b>rel1.x</b>.
  #  Build and test.
  #  Build API docs and check-in.
  #  Replace all <em>ckout</em> in docs with <em>rel1.x</em>.
  #  Update <em>wiki/changelog.wiki</em> and <em>wiki/relnotes.wiki</em>.
  #  Update <b>News</b> section of <em>wiki/home.wiki</em> plus any other docs
     that need updating.
  #  Update version number in <tt>minxlib/version.cc</tt> and commit --tag as
     vA.B.C (where major number A, minor number B, and patch level C all match

     the new version number assigned in <tt>minxlib/version.cc</tt>).
  #  Push to [http://chiselapp.com|chiselapp].




|
|
|
>
|





|
|
|
>
|
|

|
|
>



|
|
|
|
|
>



|
|
|
|
|

|
|
|
|
>










|
|
|
|
>
|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<title>Development Process</title>

<h1>About this Page</h1>

Minx is not a large project. In all likelihood, I will be the only
developer and its only user (well, there may be at most a handful of
others who might use it). Given that, the project is not well-served by a
heavy-duty process and nor does it have one. However, there is some
method to the madness...

<hr>

<h1>Development and Branching Policy</h1>

<b>dev1.x</b> is the main development branch. Usually, development will
proceed linearly on this branch. However, if there are others
contributing to the project and/or there is a major feature that will
continue for a while, we will create feature branches off off
<b>dev1.x</b>. When the feature is done, the branch will be merged onto
<b>dev1.x</b> and then, usually, closed.

<b>rel1.x</b> is the release branch. When we are ready to make a release,
we will merge from <b>dev1.x</b> and then tag (after tweaking as
required).

<h2>Release-only Artifacts</h2>

There are certain things that live only on the release branch. For
example, the version numbering API, the changelog, release notes, and so
on. These things are never merged into the development branches.
Consequently, no development ever occurs on the <b>rel1.x</b> branch. We
only merge to <b>rel1.x</b>, never from it to <b>dev1.x</b> or some other
branch.

<h3>Hotfixes</h3>

If we find a bug on <b>rel1.x</b> that needs an urgent fix, we will
branch off off <b>dev1.x</b> at the point where that particular release
was made, apply the fix on this "hotfix" branch and then merge the hotfix
branch to both <b>dev1.x</b> and <b>rel1.x</b> (tagging the release by
increasing the version number's patch level).

Although this approach is a little more complicated and results in a
slightly messier revision history, it allows us to maintain certain
release-only items (changelog, version numbering API, etc.) solely on
<b>rel1.x</b> without accidentally getting them onto a development branch
(where they do not belong).

<hr>

<h1>Making a Release</h1>

  #  Merge from <b>dev1.x</b> to <b>rel1.x</b>.
  #  Build and test.
  #  Build API docs and check-in.
  #  Replace all <em>ckout</em> in docs with <em>rel1.x</em>.
  #  Update <em>wiki/changelog.wiki</em> and <em>wiki/relnotes.wiki</em>.
  #  Update <b>News</b> section of <em>wiki/home.wiki</em> plus any other
     docs that need updating.
  #  Update version number in <tt>minxlib/version.cc</tt> and commit --tag
     as vA.B.C (where major number A, minor number B, and patch level C
     all match the new version number assigned in
     <tt>minxlib/version.cc</tt>).
  #  Push to [http://chiselapp.com|chiselapp].

Changes to wiki/todo.wiki.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

15
16
17
18


19
20
21
22
23
24
25
26
<title>TODO</title>

<h1>Current TODO List</h1>

To get to [/doc/dev1.x/wiki/milestones.wiki#v01x|version 0.1.6], the
following tasks have to be completed:

<h2>DONE</h2>

  *  To make key bindings more Emacs-like, add support for treating 'A'
     as short-hand for ALT and 'M' as META.

  *  Move focus changing logic from <tt>minx.core.xevents.on_key_press()</tt>
     to <tt>minx.core.wm.focus_next()</tt> and <tt>focus_prev()</tt>.


  *  Remove hard-coded <tt>F1</tt> for focus cycling and test defaults
     (i.e., M1-Tab and S-M1-Tab). Also: customize key bindings (F1 and
     S-F1 for focusing instead of defaults).



  *  There should be a way to disable/replace existing/default key
     bindings.

  *  Document this unholy mess: Doxygen for code, HOWTO describing
     key bindings, updated FAQ, etc.

<h2>PENDING</h2>




|




|
<

<
<
>

<
<
<
>
>

<
<
<
<
<
<

1
2
3
4
5
6
7
8
9
10

11


12
13



14
15
16






17
<title>TODO</title>

<h1>Current TODO List</h1>

To get to [/doc/dev1.x/wiki/milestones.wiki#v02x|version 0.2.0], the
following tasks have to be completed:

<h2>DONE</h2>

  *  Update design notes.




  *  Update home page.




  *  Add rectangle to full constructor. Test by leaving some space on the
     right on the right monitor of a dual-monitor test.







<h2>PENDING</h2>