| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Copyright 2011, 2012, 2013, 2014, 2016 Kevin Ryde | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # This file is part of X11-Protocol-Other. | 
| 4 |  |  |  |  |  |  | # | 
| 5 |  |  |  |  |  |  | # X11-Protocol-Other is free software; you can redistribute it and/or modify | 
| 6 |  |  |  |  |  |  | # it under the terms of the GNU General Public License as published by the | 
| 7 |  |  |  |  |  |  | # Free Software Foundation; either version 3, or (at your option) any later | 
| 8 |  |  |  |  |  |  | # version. | 
| 9 |  |  |  |  |  |  | # | 
| 10 |  |  |  |  |  |  | # X11-Protocol-Other is distributed in the hope that it will be useful, but | 
| 11 |  |  |  |  |  |  | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | 
| 12 |  |  |  |  |  |  | # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
| 13 |  |  |  |  |  |  | # for more details. | 
| 14 |  |  |  |  |  |  | # | 
| 15 |  |  |  |  |  |  | # You should have received a copy of the GNU General Public License along | 
| 16 |  |  |  |  |  |  | # with X11-Protocol-Other.  If not, see . | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | # /usr/share/doc/xorg-docs/icccm/icccm.txt.gz | 
| 20 |  |  |  |  |  |  | # /usr/share/doc/xorg-docs/ctext/ctext.txt.gz | 
| 21 |  |  |  |  |  |  | # | 
| 22 |  |  |  |  |  |  | # /usr/include/X11/Xutil.h | 
| 23 |  |  |  |  |  |  | #    Xlib structs. | 
| 24 |  |  |  |  |  |  | # | 
| 25 |  |  |  |  |  |  | # http://www.pps.univ-paris-diderot.fr/%7Ejch/software/UTF8_STRING/ | 
| 26 |  |  |  |  |  |  | # http://www.pps.univ-paris-diderot.fr/%7Ejch/software/UTF8_STRING/UTF8_STRING.text | 
| 27 |  |  |  |  |  |  | # /so/netwm/UTF8_STRING.text | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 4 |  |  | 4 |  | 11021 | BEGIN { require 5 } | 
| 30 |  |  |  |  |  |  | package X11::Protocol::WM; | 
| 31 | 4 |  |  | 4 |  | 14 | use strict; | 
|  | 4 |  |  |  |  | 3 |  | 
|  | 4 |  |  |  |  | 61 |  | 
| 32 | 4 |  |  | 4 |  | 12 | use Carp; | 
|  | 4 |  |  |  |  | 4 |  | 
|  | 4 |  |  |  |  | 178 |  | 
| 33 | 4 |  |  | 4 |  | 1562 | use X11::AtomConstants; | 
|  | 4 |  |  |  |  | 4 |  | 
|  | 4 |  |  |  |  | 125 |  | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 4 |  |  | 4 |  | 15 | use vars '$VERSION', '@ISA', '@EXPORT_OK'; | 
|  | 4 |  |  |  |  | 4 |  | 
|  | 4 |  |  |  |  | 163 |  | 
| 36 |  |  |  |  |  |  | $VERSION = 30; | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 4 |  |  | 4 |  | 10 | use Exporter; | 
|  | 4 |  |  |  |  | 3 |  | 
|  | 4 |  |  |  |  | 269 |  | 
| 39 |  |  |  |  |  |  | @ISA = ('Exporter'); | 
| 40 |  |  |  |  |  |  | @EXPORT_OK = qw( | 
| 41 |  |  |  |  |  |  | frame_window_to_client | 
| 42 |  |  |  |  |  |  | root_to_virtual_root | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | change_wm_hints | 
| 45 |  |  |  |  |  |  | change_net_wm_state | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | get_wm_icon_size | 
| 48 |  |  |  |  |  |  | get_wm_hints | 
| 49 |  |  |  |  |  |  | get_wm_state | 
| 50 |  |  |  |  |  |  | get_net_frame_extents | 
| 51 |  |  |  |  |  |  | get_net_wm_state | 
| 52 |  |  |  |  |  |  | set_text_property | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | set_wm_class | 
| 55 |  |  |  |  |  |  | set_wm_client_machine | 
| 56 |  |  |  |  |  |  | set_wm_client_machine_from_syshostname | 
| 57 |  |  |  |  |  |  | set_wm_command | 
| 58 |  |  |  |  |  |  | set_wm_hints | 
| 59 |  |  |  |  |  |  | set_wm_name | 
| 60 |  |  |  |  |  |  | set_wm_normal_hints | 
| 61 |  |  |  |  |  |  | set_wm_icon_name | 
| 62 |  |  |  |  |  |  | set_wm_protocols | 
| 63 |  |  |  |  |  |  | set_wm_transient_for | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | set_motif_wm_hints | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | set_net_wm_pid | 
| 68 |  |  |  |  |  |  | set_net_wm_state | 
| 69 |  |  |  |  |  |  | set_net_wm_user_time | 
| 70 |  |  |  |  |  |  | set_net_wm_window_type | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | pack_wm_hints | 
| 73 |  |  |  |  |  |  | pack_wm_size_hints | 
| 74 |  |  |  |  |  |  | pack_motif_wm_hints | 
| 75 |  |  |  |  |  |  | unpack_wm_hints | 
| 76 |  |  |  |  |  |  | unpack_wm_state | 
| 77 |  |  |  |  |  |  | aspect_to_num_den | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | iconify | 
| 80 |  |  |  |  |  |  | withdraw | 
| 81 |  |  |  |  |  |  | ); | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | # uncomment this to run the ### lines | 
| 84 |  |  |  |  |  |  | # use Smart::Comments; | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 88 |  |  |  |  |  |  | # shared bits | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | BEGIN { | 
| 91 | 4 | 50 | 33 | 4 |  | 379 | eval 'utf8->can("is_utf8") && *is_utf8 = \&utf8::is_utf8'   # 5.8.1 | 
|  | 4 |  | 33 | 4 |  | 1798 |  | 
|  | 4 |  |  |  |  | 26776 |  | 
|  | 4 |  |  |  |  | 164 |  | 
| 92 |  |  |  |  |  |  | || eval 'use Encode "is_utf8"; 1'                         # 5.8.0 | 
| 93 |  |  |  |  |  |  | || eval 'sub is_utf8 { 0 }; 1'                          # 5.6 fallback | 
| 94 |  |  |  |  |  |  | || die 'Oops, cannot create is_utf8() subr: ',$@; | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  | ### \&is_utf8 | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | sub set_text_property { | 
| 99 | 0 |  |  | 0 | 1 |  | my ($X, $window, $prop, $str) = @_; | 
| 100 | 0 | 0 |  |  |  |  | if (defined $str) { | 
| 101 | 0 |  |  |  |  |  | my $type; | 
| 102 | 0 |  |  |  |  |  | ($type, $str) = _to_TEXT ($X, $str); | 
| 103 | 0 |  |  |  |  |  | $X->ChangeProperty ($window, | 
| 104 |  |  |  |  |  |  | $prop,  # prop name | 
| 105 |  |  |  |  |  |  | $type,  # type | 
| 106 |  |  |  |  |  |  | 8,      # format | 
| 107 |  |  |  |  |  |  | 'Replace', | 
| 108 |  |  |  |  |  |  | $str); | 
| 109 |  |  |  |  |  |  | } else { | 
| 110 | 0 |  |  |  |  |  | $X->DeleteProperty ($window, $prop); | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | # Maybe ... | 
| 115 |  |  |  |  |  |  | # | 
| 116 |  |  |  |  |  |  | # =item C<$str = _to_STRING ($str)> | 
| 117 |  |  |  |  |  |  | # | 
| 118 |  |  |  |  |  |  | # Convert C<$str> to latin-1 bytes for use in a STRING property.  If C<$str> | 
| 119 |  |  |  |  |  |  | # is already bytes then they're presumed to be latin-1.  If C<$str> is Perl | 
| 120 |  |  |  |  |  |  | # 5.8 wide chars then it's converted with the Encode module, and C | 
| 121 |  |  |  |  |  |  | # if cannot be represented as a STRING. | 
| 122 |  |  |  |  |  |  | # | 
| 123 |  |  |  |  |  |  | sub _to_STRING { | 
| 124 | 0 |  |  | 0 |  |  | my ($str) = @_; | 
| 125 | 0 | 0 |  |  |  |  | if (is_utf8($str)) { | 
| 126 | 0 |  |  |  |  |  | require Encode; | 
| 127 |  |  |  |  |  |  | # croak in the interests of not letting bad values go through unnoticed, | 
| 128 |  |  |  |  |  |  | # nor letting a mangled name be stored | 
| 129 | 0 |  |  |  |  |  | return Encode::encode ('iso-8859-1', $str, Encode::FB_CROAK()); | 
| 130 |  |  |  |  |  |  | } else { | 
| 131 | 0 |  |  |  |  |  | return $str; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | # Maybe ... | 
| 136 |  |  |  |  |  |  | # | 
| 137 |  |  |  |  |  |  | # =item C<($atom, $bytes) = _to_TEXT ($X, $str)> | 
| 138 |  |  |  |  |  |  | # | 
| 139 |  |  |  |  |  |  | # Convert C<$str> to either C or C per L | 
| 140 |  |  |  |  |  |  | # Properties> above.  The returned C<$atom> (an integer) is the either | 
| 141 |  |  |  |  |  |  | # C or C and C<$bytes> are bytes of that type. | 
| 142 |  |  |  |  |  |  | # | 
| 143 |  |  |  |  |  |  | sub _to_TEXT { | 
| 144 | 0 |  |  | 0 |  |  | my ($X, $str) = @_; | 
| 145 | 0 | 0 |  |  |  |  | if (! is_utf8($str)) { | 
| 146 |  |  |  |  |  |  | # bytes or pre-5.8 taken to be latin-1 | 
| 147 | 0 |  |  |  |  |  | return (X11::AtomConstants::STRING(), $str); | 
| 148 |  |  |  |  |  |  | } | 
| 149 | 0 |  |  |  |  |  | require Encode; | 
| 150 |  |  |  |  |  |  | { | 
| 151 | 0 |  |  |  |  |  | my $input = $str; # don't clobber $str | 
|  | 0 |  |  |  |  |  |  | 
| 152 | 0 |  |  |  |  |  | my $bytes = Encode::encode ('iso-8859-1', $input, Encode::FB_QUIET()); | 
| 153 | 0 | 0 |  |  |  |  | if (length($input) == 0) { | 
| 154 |  |  |  |  |  |  | # latin-1 suffices | 
| 155 | 0 |  |  |  |  |  | return (X11::AtomConstants::STRING(), $bytes); | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  | } | 
| 158 | 0 |  |  |  |  |  | require Encode::X11; | 
| 159 | 0 |  |  |  |  |  | return ($X->atom('COMPOUND_TEXT'), | 
| 160 |  |  |  |  |  |  | Encode::encode ('x11-compound-text', $str, Encode::FB_WARN())); | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | # Set a property on $window (integer XID) to a single CARD32 integer value. | 
| 164 |  |  |  |  |  |  | # $prop is the property (integer atom ID). | 
| 165 |  |  |  |  |  |  | # $type is the property type (integer atom ID). | 
| 166 |  |  |  |  |  |  | # $value is a 32-bit integer to store, or undef to delete the property. | 
| 167 |  |  |  |  |  |  | # | 
| 168 |  |  |  |  |  |  | # The ICCCM or similar specification will say what C<$type> should be in a | 
| 169 |  |  |  |  |  |  | # property.  Often there's only one type, but in any case C<$type> indicates | 
| 170 |  |  |  |  |  |  | # what has been stored.  This might be for example the atom for "PIXMAP" if | 
| 171 |  |  |  |  |  |  | # $value is a pixmap XID.  Things which are counts or numbers are usually | 
| 172 |  |  |  |  |  |  | # the atom "CARDINAL". | 
| 173 |  |  |  |  |  |  | # | 
| 174 |  |  |  |  |  |  | sub _set_card32_property { | 
| 175 | 0 |  |  | 0 |  |  | my ($X, $window, $prop, $type, $value) = @_; | 
| 176 | 0 | 0 |  |  |  |  | if (defined $value) { | 
| 177 | 0 |  |  |  |  |  | $X->ChangeProperty ($window, | 
| 178 |  |  |  |  |  |  | $prop,  # prop name | 
| 179 |  |  |  |  |  |  | $type,  # type | 
| 180 |  |  |  |  |  |  | 32,     # format | 
| 181 |  |  |  |  |  |  | 'Replace', | 
| 182 |  |  |  |  |  |  | pack ('L', $value)); | 
| 183 |  |  |  |  |  |  | } else { | 
| 184 | 0 |  |  |  |  |  | $X->DeleteProperty ($window, $prop); | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | # or maybe $X->num('IDorNone',$xid) | 
| 189 |  |  |  |  |  |  | #          $X->num('XID',$xid) | 
| 190 |  |  |  |  |  |  | sub _num_none { | 
| 191 | 0 |  |  | 0 |  |  | my ($xid) = @_; | 
| 192 | 0 | 0 | 0 |  |  |  | if (defined $xid && $xid eq "None") { | 
| 193 | 0 |  |  |  |  |  | return 0; | 
| 194 |  |  |  |  |  |  | } else { | 
| 195 | 0 |  |  |  |  |  | return $xid; | 
| 196 |  |  |  |  |  |  | } | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | # or maybe $X->interp('IDorNone',$xid) or 'XIDorNone' | 
| 200 |  |  |  |  |  |  | sub _none_interp { | 
| 201 | 0 |  |  | 0 |  |  | my ($X, $xid) = @_; | 
| 202 | 0 | 0 | 0 |  |  |  | if ($X->{'do_interp'} && $xid == 0) { | 
| 203 | 0 |  |  |  |  |  | return 'None'; | 
| 204 |  |  |  |  |  |  | } else { | 
| 205 | 0 |  |  |  |  |  | return $xid; | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | # return $root or if that's undef then lookup root of $window | 
| 210 |  |  |  |  |  |  | sub _root_for_window { | 
| 211 | 0 |  |  | 0 |  |  | my ($X, $window, $root) = @_; | 
| 212 | 0 | 0 |  |  |  |  | if (! defined $root) { | 
| 213 | 0 |  |  |  |  |  | ($root) = $X->QueryTree($window); | 
| 214 |  |  |  |  |  |  | } | 
| 215 | 0 |  |  |  |  |  | return $root; | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 219 |  |  |  |  |  |  | # frame_window_to_client() | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | # /usr/share/doc/libxmu-headers/Xmu.txt.gz for XmuClientWindow() | 
| 222 |  |  |  |  |  |  | # https://bugs.freedesktop.org/show_bug.cgi?id=7474 | 
| 223 |  |  |  |  |  |  | #     XmuClientWindow() bottom-up was hurting fluxbox and probably ion, pekwm | 
| 224 |  |  |  |  |  |  | # | 
| 225 |  |  |  |  |  |  | sub frame_window_to_client { | 
| 226 | 0 |  |  | 0 | 1 |  | my ($X, $frame) = @_; | 
| 227 |  |  |  |  |  |  |  | 
| 228 | 0 |  |  |  |  |  | my @search = ($frame); | 
| 229 | 0 |  |  |  |  |  | my $property = $X->atom('WM_STATE'); | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | # ENHANCE-ME: do three reqs in parallel, better yet all reqs for an | 
| 232 |  |  |  |  |  |  | # @search depth level in parallel | 
| 233 |  |  |  |  |  |  |  | 
| 234 | 0 |  |  |  |  |  | my $count = 0; | 
| 235 | 0 |  |  |  |  |  | OUTER: foreach (1 .. 5) {   # limit search depth for safety | 
| 236 | 0 |  |  |  |  |  | my $child; | 
| 237 | 0 |  |  |  |  |  | foreach $child (splice @search) {   # breadth-first search | 
| 238 |  |  |  |  |  |  | ### look at: sprintf '0x%X', $child | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 0 | 0 |  |  |  |  | if ($count++ > 50) { | 
| 241 |  |  |  |  |  |  | ### abandon search at count: $count | 
| 242 | 0 |  |  |  |  |  | return undef; | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | { | 
| 246 | 0 |  |  |  |  |  | my $ret = $X->robust_req ('GetWindowAttributes', $child); | 
| 247 | 0 | 0 |  |  |  |  | if (! ref $ret) { | 
| 248 |  |  |  |  |  |  | ### some error, skip this child | 
| 249 | 0 |  |  |  |  |  | next; | 
| 250 |  |  |  |  |  |  | } | 
| 251 | 0 |  |  |  |  |  | my %attr = @$ret; | 
| 252 |  |  |  |  |  |  | ### map_state: $attr{'map_state'} | 
| 253 | 0 | 0 |  |  |  |  | if ($attr{'map_state'} ne 'Viewable') { | 
| 254 |  |  |  |  |  |  | ### not viewable, skip | 
| 255 | 0 |  |  |  |  |  | next; | 
| 256 |  |  |  |  |  |  | } | 
| 257 |  |  |  |  |  |  | } | 
| 258 |  |  |  |  |  |  | { | 
| 259 | 0 |  |  |  |  |  | my $ret = $X->robust_req ('GetProperty', | 
|  | 0 |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | $child, $property, 'AnyPropertyType', | 
| 261 |  |  |  |  |  |  | 0,  # offset | 
| 262 |  |  |  |  |  |  | 0,  # length | 
| 263 |  |  |  |  |  |  | 0); # delete; | 
| 264 | 0 | 0 |  |  |  |  | if (! ref $ret) { | 
| 265 |  |  |  |  |  |  | ### some error, skip this child | 
| 266 | 0 |  |  |  |  |  | next; | 
| 267 |  |  |  |  |  |  | } | 
| 268 | 0 |  |  |  |  |  | my ($value, $type, $format, $bytes_after) = @$ret; | 
| 269 | 0 | 0 |  |  |  |  | if ($type) { | 
| 270 |  |  |  |  |  |  | ### found | 
| 271 | 0 |  |  |  |  |  | return $child; | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  | { | 
| 275 | 0 |  |  |  |  |  | my $ret = $X->robust_req ('QueryTree', $child); | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 276 | 0 | 0 |  |  |  |  | if (ref $ret) { | 
| 277 | 0 |  |  |  |  |  | my ($root, $parent, @children) = @$ret; | 
| 278 |  |  |  |  |  |  | ### push children: @children | 
| 279 |  |  |  |  |  |  | # @children are in bottom up order, prefer the topmost | 
| 280 | 0 |  |  |  |  |  | push @search, reverse @children; | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  | } | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  | ### not found | 
| 286 | 0 |  |  |  |  |  | return undef; | 
| 287 |  |  |  |  |  |  | } | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 291 |  |  |  |  |  |  | # root_to_virtual_root() | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | # ENHANCE-ME: Could do all the GetProperty checks in parallel. | 
| 294 |  |  |  |  |  |  | # Could intern the VROOT atom during the QueryTree too. | 
| 295 |  |  |  |  |  |  | # | 
| 296 |  |  |  |  |  |  | sub root_to_virtual_root { | 
| 297 | 0 |  |  | 0 | 1 |  | my ($X, $root) = @_; | 
| 298 |  |  |  |  |  |  | ### root_to_virtual_root(): $root | 
| 299 |  |  |  |  |  |  |  | 
| 300 | 0 |  |  |  |  |  | my ($root_root, $root_parent, @toplevels) = $X->QueryTree($root); | 
| 301 | 0 |  |  |  |  |  | my $toplevel; | 
| 302 | 0 |  |  |  |  |  | foreach $toplevel (@toplevels) { | 
| 303 |  |  |  |  |  |  | ### $toplevel | 
| 304 | 0 |  |  |  |  |  | my @ret = $X->robust_req ('GetProperty', | 
| 305 |  |  |  |  |  |  | $toplevel, | 
| 306 |  |  |  |  |  |  | $X->atom('__SWM_VROOT'), | 
| 307 |  |  |  |  |  |  | X11::AtomConstants::WINDOW(),  # type | 
| 308 |  |  |  |  |  |  | 0,  # offset | 
| 309 |  |  |  |  |  |  | 1,  # length x 32bits | 
| 310 |  |  |  |  |  |  | 0); # delete; | 
| 311 |  |  |  |  |  |  | ### @ret | 
| 312 | 0 | 0 |  |  |  |  | next unless ref $ret[0]; # ignore errors from toplevels destroyed etc | 
| 313 |  |  |  |  |  |  |  | 
| 314 | 0 |  |  |  |  |  | my ($value, $type, $format, $bytes_after) = @{$ret[0]}; | 
|  | 0 |  |  |  |  |  |  | 
| 315 | 0 | 0 |  |  |  |  | if (my $vroot = unpack 'L', $value) { | 
| 316 |  |  |  |  |  |  | ### found: $vroot | 
| 317 | 0 |  |  |  |  |  | return $vroot; | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  | } | 
| 320 | 0 |  |  |  |  |  | return $root; | 
| 321 |  |  |  |  |  |  | } | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 325 |  |  |  |  |  |  | # WM_CLASS | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | sub set_wm_class { | 
| 328 | 0 |  |  | 0 | 1 |  | my ($X, $window, $instance, $class) = @_; | 
| 329 | 0 | 0 |  |  |  |  | if (defined $instance) { | 
| 330 | 0 |  |  |  |  |  | my $str = _to_STRING($instance)."\0"._to_STRING($class)."\0"; | 
| 331 | 0 |  |  |  |  |  | $X->ChangeProperty($window, | 
| 332 |  |  |  |  |  |  | X11::AtomConstants::WM_CLASS(), # prop | 
| 333 |  |  |  |  |  |  | X11::AtomConstants::STRING(),   # type | 
| 334 |  |  |  |  |  |  | 8,                              # byte format | 
| 335 |  |  |  |  |  |  | 'Replace', | 
| 336 |  |  |  |  |  |  | $str); | 
| 337 |  |  |  |  |  |  | } else { | 
| 338 | 0 |  |  |  |  |  | $X->DeleteProperty ($window, X11::AtomConstants::WM_CLASS()); | 
| 339 |  |  |  |  |  |  | } | 
| 340 |  |  |  |  |  |  | } | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 344 |  |  |  |  |  |  | # WM_CLIENT_MACHINE | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | sub set_wm_client_machine { | 
| 347 | 0 |  |  | 0 | 1 |  | my ($X, $window, $hostname) = @_; | 
| 348 | 0 |  |  |  |  |  | set_text_property ($X, $window, | 
| 349 |  |  |  |  |  |  | X11::AtomConstants::WM_CLIENT_MACHINE(), $hostname); | 
| 350 |  |  |  |  |  |  | } | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | sub set_wm_client_machine_from_syshostname { | 
| 353 | 0 |  |  | 0 | 1 |  | my ($X, $window) = @_; | 
| 354 | 0 |  |  |  |  |  | require Sys::Hostname; | 
| 355 | 0 |  |  |  |  |  | set_wm_client_machine ($X, $window, eval { Sys::Hostname::hostname() }); | 
|  | 0 |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | } | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 360 |  |  |  |  |  |  | # WM_COMMAND | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | sub set_wm_command { | 
| 363 | 0 |  |  | 0 | 1 |  | my $X = shift; | 
| 364 | 0 |  |  |  |  |  | my $window = shift; | 
| 365 |  |  |  |  |  |  |  | 
| 366 | 0 | 0 | 0 |  |  |  | if (@_ && ! defined $_[0]) { | 
| 367 |  |  |  |  |  |  | # this not documented ... | 
| 368 | 0 |  |  |  |  |  | $X->DeleteProperty ($window, X11::AtomConstants::WM_COMMAND()); | 
| 369 | 0 |  |  |  |  |  | return; | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | # cf join() gives a wide-char result if any parts wide, upgrading byte | 
| 373 |  |  |  |  |  |  | # strings as if they were latin-1 | 
| 374 | 0 |  |  |  |  |  | my $value = ''; | 
| 375 | 0 |  |  |  |  |  | my $type = X11::AtomConstants::STRING(); | 
| 376 | 0 |  |  |  |  |  | my $str; | 
| 377 | 0 |  |  |  |  |  | foreach $str (@_) { | 
| 378 | 0 |  |  |  |  |  | my ($atom, $bytes) = _to_TEXT($X,$str); | 
| 379 | 0 | 0 |  |  |  |  | if ($atom != X11::AtomConstants::STRING()) { | 
| 380 | 0 |  |  |  |  |  | $type = $atom;  # COMPOUND_TEXT if any part needs COMPOUND_TEXT | 
| 381 |  |  |  |  |  |  | } | 
| 382 | 0 |  |  |  |  |  | $value .= "$bytes\0"; | 
| 383 |  |  |  |  |  |  | } | 
| 384 | 0 | 0 |  |  |  |  | if ($value eq "\0") { | 
| 385 | 0 |  |  |  |  |  | $value = "";  # this not documented ... | 
| 386 |  |  |  |  |  |  | # C<$command> can be an empty string "" to mean no known command as a | 
| 387 |  |  |  |  |  |  | # reply to C ... maybe | 
| 388 |  |  |  |  |  |  | } | 
| 389 | 0 |  |  |  |  |  | $X->ChangeProperty ($window, | 
| 390 |  |  |  |  |  |  | X11::AtomConstants::WM_COMMAND(), # prop name | 
| 391 |  |  |  |  |  |  | $type,  # type | 
| 392 |  |  |  |  |  |  | 8,      # format | 
| 393 |  |  |  |  |  |  | 'Replace', | 
| 394 |  |  |  |  |  |  | $value); | 
| 395 |  |  |  |  |  |  | } | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 399 |  |  |  |  |  |  | # WM_ICON_SIZE | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | sub get_wm_icon_size { | 
| 402 | 0 |  |  | 0 | 1 |  | my ($X, $root) = @_; | 
| 403 | 0 | 0 |  |  |  |  | if (! defined $root) { | 
| 404 | 0 |  |  |  |  |  | $root = $X->root; | 
| 405 |  |  |  |  |  |  | } | 
| 406 | 0 |  |  |  |  |  | my ($value, $type, $format, $bytes_after) | 
| 407 |  |  |  |  |  |  | = $X->GetProperty ($root, | 
| 408 |  |  |  |  |  |  | X11::AtomConstants::WM_ICON_SIZE(),  # property | 
| 409 |  |  |  |  |  |  | X11::AtomConstants::WM_ICON_SIZE(),  # type | 
| 410 |  |  |  |  |  |  | 0,   # offset | 
| 411 |  |  |  |  |  |  | 6,   # length CARD32s | 
| 412 |  |  |  |  |  |  | 0);  # delete; | 
| 413 | 0 | 0 |  |  |  |  | if ($format == 32) { | 
| 414 | 0 |  |  |  |  |  | return unpack 'L6', $value; | 
| 415 |  |  |  |  |  |  | } else { | 
| 416 | 0 |  |  |  |  |  | return; | 
| 417 |  |  |  |  |  |  | } | 
| 418 |  |  |  |  |  |  | } | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 422 |  |  |  |  |  |  | # WM_HINTS | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | sub set_wm_hints { | 
| 425 | 0 |  |  | 0 | 1 |  | my $X = shift; | 
| 426 | 0 |  |  |  |  |  | my $window = shift; | 
| 427 |  |  |  |  |  |  | ### set_wm_hints(): @_ | 
| 428 |  |  |  |  |  |  | ### set cards: map {sprintf '%#x',$_} unpack 'L*', pack_wm_hints($X,@_) | 
| 429 | 0 |  |  |  |  |  | $X->ChangeProperty($window, | 
| 430 |  |  |  |  |  |  | X11::AtomConstants::WM_HINTS(), # prop name | 
| 431 |  |  |  |  |  |  | X11::AtomConstants::WM_HINTS(), # type | 
| 432 |  |  |  |  |  |  | 32,           # format | 
| 433 |  |  |  |  |  |  | 'Replace', | 
| 434 |  |  |  |  |  |  | pack_wm_hints($X, @_)); | 
| 435 |  |  |  |  |  |  | } | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | sub get_wm_hints { | 
| 438 | 0 |  |  | 0 | 1 |  | my ($X, $window) = @_; | 
| 439 | 0 |  |  |  |  |  | my ($value, $type, $format, $bytes_after) | 
| 440 |  |  |  |  |  |  | = $X->GetProperty ($window, | 
| 441 |  |  |  |  |  |  | X11::AtomConstants::WM_HINTS(), # prop name | 
| 442 |  |  |  |  |  |  | X11::AtomConstants::WM_HINTS(), # type | 
| 443 |  |  |  |  |  |  | 0,             # offset | 
| 444 |  |  |  |  |  |  | 9,             # length($format), of CARD32 | 
| 445 |  |  |  |  |  |  | 0);            # no delete | 
| 446 | 0 | 0 |  |  |  |  | if ($format == 32) { | 
| 447 |  |  |  |  |  |  | ### got cards: map {sprintf '%#x',$_} unpack 'L*', $value | 
| 448 | 0 |  |  |  |  |  | return unpack_wm_hints ($X, $value); | 
| 449 |  |  |  |  |  |  | } else { | 
| 450 | 0 |  |  |  |  |  | return; | 
| 451 |  |  |  |  |  |  | } | 
| 452 |  |  |  |  |  |  | } | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | sub change_wm_hints { | 
| 455 | 0 |  |  | 0 | 1 |  | my $X = shift; | 
| 456 | 0 |  |  |  |  |  | my $window = shift; | 
| 457 | 0 |  |  |  |  |  | set_wm_hints ($X, $window, get_wm_hints($X,$window), @_); | 
| 458 |  |  |  |  |  |  | } | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | { | 
| 461 |  |  |  |  |  |  | my $format = 'LLLLLllLL'; | 
| 462 |  |  |  |  |  |  | # The C hint was called "visible" in X11R5.  The name "urgency" | 
| 463 |  |  |  |  |  |  | # is used here per X11R6.  The actual field sent and received is the same. | 
| 464 |  |  |  |  |  |  | # | 
| 465 |  |  |  |  |  |  | my %key_to_flag = (input         => 1, | 
| 466 |  |  |  |  |  |  | initial_state => 2, | 
| 467 |  |  |  |  |  |  | icon_pixmap   => 4, | 
| 468 |  |  |  |  |  |  | icon_window   => 8, | 
| 469 |  |  |  |  |  |  | icon_x        => 16, | 
| 470 |  |  |  |  |  |  | icon_y        => 16, | 
| 471 |  |  |  |  |  |  | icon_mask     => 32, | 
| 472 |  |  |  |  |  |  | window_group  => 64, | 
| 473 |  |  |  |  |  |  | # message     => 128, # in the code, obsolete | 
| 474 |  |  |  |  |  |  | # urgency     => 256, # in the code | 
| 475 |  |  |  |  |  |  | ); | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | sub pack_wm_hints { | 
| 478 | 0 |  |  | 0 | 1 |  | my ($X, %hint) = @_; | 
| 479 |  |  |  |  |  |  | ### pack_wm_hints(): %hint | 
| 480 | 0 |  |  |  |  |  | my $flags = 0; | 
| 481 | 0 | 0 |  |  |  |  | if (delete $hint{'message'}) { | 
| 482 | 0 |  |  |  |  |  | $flags = 128; | 
| 483 |  |  |  |  |  |  | } | 
| 484 | 0 | 0 |  |  |  |  | if (delete $hint{'urgency'}) { | 
| 485 | 0 |  |  |  |  |  | $flags |= 256; | 
| 486 |  |  |  |  |  |  | } | 
| 487 | 0 |  |  |  |  |  | my $key; | 
| 488 | 0 |  |  |  |  |  | foreach $key (keys %hint) { | 
| 489 | 0 |  | 0 |  |  |  | my $flag_bit = $key_to_flag{$key} | 
| 490 |  |  |  |  |  |  | || croak "Unknown WM_HINT field: ",$key; | 
| 491 | 0 | 0 |  |  |  |  | if (defined $hint{$key}) { | 
| 492 | 0 |  |  |  |  |  | $flags |= $flag_bit; | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  | } | 
| 495 |  |  |  |  |  |  | return pack ($format, | 
| 496 |  |  |  |  |  |  | $flags, | 
| 497 |  |  |  |  |  |  | $hint{'input'} || 0,                       # CARD32 bool | 
| 498 |  |  |  |  |  |  | _wmstate_num($hint{'initial_state'}) || 0, # CARD32 enum | 
| 499 |  |  |  |  |  |  | _num_none($hint{'icon_pixmap'}) || 0,      # PIXMAP | 
| 500 |  |  |  |  |  |  | _num_none($hint{'icon_window'}) || 0,      # WINDOW | 
| 501 |  |  |  |  |  |  | $hint{'icon_x'} || 0,                      # INT32 | 
| 502 |  |  |  |  |  |  | $hint{'icon_y'} || 0,                      # INT32 | 
| 503 |  |  |  |  |  |  | _num_none($hint{'icon_mask'}) || 0,        # PIXMAP | 
| 504 | 0 |  | 0 |  |  |  | _num_none($hint{'window_group'}) || 0);    # WINDOW | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 505 |  |  |  |  |  |  | } | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | # X11R2 Xlib had a bug where XSetWMHints() set a WM_HINTS property to only | 
| 508 |  |  |  |  |  |  | # 8 CARD32s, chopping off the window_group field.  This was due to | 
| 509 |  |  |  |  |  |  | # Xatomtype.h NumPropWMHintsElements being 8 instead of 9.  If the length | 
| 510 |  |  |  |  |  |  | # of $bytes here is only 8 then ignore any window_group bit in the flags | 
| 511 |  |  |  |  |  |  | # and don't return a window_group field.  X11R2 source available at | 
| 512 |  |  |  |  |  |  | # http://ftp.x.org/pub/X11R2/X.V11R2.tar.gz | 
| 513 |  |  |  |  |  |  | # | 
| 514 |  |  |  |  |  |  | my @keys = ('input', | 
| 515 |  |  |  |  |  |  | 'initial_state', | 
| 516 |  |  |  |  |  |  | 'icon_pixmap', | 
| 517 |  |  |  |  |  |  | 'icon_window', | 
| 518 |  |  |  |  |  |  | 'icon_x', | 
| 519 |  |  |  |  |  |  | 'icon_y', | 
| 520 |  |  |  |  |  |  | 'icon_mask', | 
| 521 |  |  |  |  |  |  | 'window_group', | 
| 522 |  |  |  |  |  |  | # 'message',      # in the code, and obsolete ... | 
| 523 |  |  |  |  |  |  | # 'urgency',      # in the code | 
| 524 |  |  |  |  |  |  | ); | 
| 525 |  |  |  |  |  |  | my @interp = (\&_unchanged,                          # input | 
| 526 |  |  |  |  |  |  | \&_wmstate_interp,   # initial_state | 
| 527 |  |  |  |  |  |  | \&_none_interp,      # icon_pixmap | 
| 528 |  |  |  |  |  |  | \&_none_interp,      # icon_window | 
| 529 |  |  |  |  |  |  | \&_unchanged,                           # icon_x | 
| 530 |  |  |  |  |  |  | \&_unchanged,                           # icon_y | 
| 531 |  |  |  |  |  |  | \&_none_interp,      # icon_mask | 
| 532 |  |  |  |  |  |  | \&_none_interp,      # window_group | 
| 533 |  |  |  |  |  |  | ); | 
| 534 |  |  |  |  |  |  | sub unpack_wm_hints { | 
| 535 | 0 |  |  | 0 | 1 |  | my ($X, $bytes) = @_; | 
| 536 |  |  |  |  |  |  | ### unpack_wm_hints(): unpack 'L*', $bytes | 
| 537 | 0 |  |  |  |  |  | my ($flags, @values) = unpack ($format, $bytes); | 
| 538 | 0 |  |  |  |  |  | my $bit = 1; | 
| 539 | 0 |  |  |  |  |  | my @ret; | 
| 540 |  |  |  |  |  |  | my $i; | 
| 541 | 0 |  |  |  |  |  | foreach $i (0 .. $#keys) { | 
| 542 | 0 |  |  |  |  |  | my $value = $values[$i]; | 
| 543 | 0 | 0 |  |  |  |  | if (! defined $value) { | 
| 544 |  |  |  |  |  |  | # if $bytes is only 8 CARD32s as from X11R2 then omit window_group | 
| 545 |  |  |  |  |  |  | # from the return | 
| 546 | 0 |  |  |  |  |  | next; | 
| 547 |  |  |  |  |  |  | } | 
| 548 | 0 | 0 |  |  |  |  | if ($flags & $bit) { | 
| 549 | 0 |  |  |  |  |  | push @ret, $keys[$i], &{$interp[$i]}($X, $value); | 
|  | 0 |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | } | 
| 551 | 0 |  |  |  |  |  | $bit <<= ($i!=4);  # icon_x,icon_y both at $bit==16 | 
| 552 |  |  |  |  |  |  | } | 
| 553 | 0 | 0 |  |  |  |  | if ($flags & 128) { | 
| 554 | 0 |  |  |  |  |  | push @ret, message => 1; | 
| 555 |  |  |  |  |  |  | } | 
| 556 | 0 | 0 |  |  |  |  | if ($flags & 256) { | 
| 557 | 0 |  |  |  |  |  | push @ret, urgency => 1; | 
| 558 |  |  |  |  |  |  | } | 
| 559 | 0 |  |  |  |  |  | return @ret; | 
| 560 |  |  |  |  |  |  | } | 
| 561 |  |  |  |  |  |  | } | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | sub _unchanged { | 
| 564 | 0 |  |  | 0 |  |  | my ($X, $value) = @_; | 
| 565 | 0 |  |  |  |  |  | return $value; | 
| 566 |  |  |  |  |  |  | } | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 570 |  |  |  |  |  |  | # WM_ICON_NAME | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | sub set_wm_icon_name { | 
| 573 | 0 |  |  | 0 | 1 |  | my ($X, $window, $name) = @_; | 
| 574 | 0 |  |  |  |  |  | set_text_property ($X, $window, X11::AtomConstants::WM_ICON_NAME(), $name); | 
| 575 |  |  |  |  |  |  | } | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 579 |  |  |  |  |  |  | # WM_NAME | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | sub set_wm_name { | 
| 582 | 0 |  |  | 0 | 1 |  | my ($X, $window, $name) = @_; | 
| 583 | 0 |  |  |  |  |  | set_text_property ($X, $window, X11::AtomConstants::WM_NAME(), $name); | 
| 584 |  |  |  |  |  |  | } | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 587 |  |  |  |  |  |  | # WM_PROTOCOLS | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  | sub set_wm_protocols { | 
| 590 | 0 |  |  | 0 | 1 |  | my $X = shift; | 
| 591 | 0 |  |  |  |  |  | my $window = shift; | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | # ENHANCE-ME: intern all atoms in one round-trip | 
| 594 | 0 |  |  |  |  |  | my $prop = $X->atom('WM_PROTOCOLS'); | 
| 595 | 0 | 0 |  |  |  |  | if (@_) { | 
| 596 | 0 |  |  |  |  |  | $X->ChangeProperty($window, | 
| 597 |  |  |  |  |  |  | $prop,                       # property | 
| 598 |  |  |  |  |  |  | X11::AtomConstants::ATOM(),  # type | 
| 599 |  |  |  |  |  |  | 32,                          # format | 
| 600 |  |  |  |  |  |  | 'Replace', | 
| 601 |  |  |  |  |  |  | pack('L*',_to_atom_nums($X,@_))); | 
| 602 |  |  |  |  |  |  | } else { | 
| 603 | 0 |  |  |  |  |  | $X->DeleteProperty ($window, $prop); | 
| 604 |  |  |  |  |  |  | } | 
| 605 |  |  |  |  |  |  | } | 
| 606 |  |  |  |  |  |  | sub _to_atom_nums { | 
| 607 | 0 |  |  | 0 |  |  | my $X = shift; | 
| 608 | 0 | 0 |  |  |  |  | return map { ($_ =~ /^\d+$/ ? $_ : $X->atom($_)) } @_; | 
|  | 0 |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  |  | 
| 612 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 613 |  |  |  |  |  |  | # WM_STATE enum | 
| 614 |  |  |  |  |  |  | # For internal use yet ... | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | { | 
| 617 |  |  |  |  |  |  | my %wmstate = (WithdrawnState => 0, | 
| 618 |  |  |  |  |  |  | DontCareState  => 0, # no longer in ICCCM | 
| 619 |  |  |  |  |  |  | NormalState    => 1, | 
| 620 |  |  |  |  |  |  | ZoomState      => 2, # no longer in ICCCM | 
| 621 |  |  |  |  |  |  | IconicState    => 3, | 
| 622 |  |  |  |  |  |  | InactiveState  => 4, # no longer in ICCCM | 
| 623 |  |  |  |  |  |  | ); | 
| 624 |  |  |  |  |  |  | sub _wmstate_num { | 
| 625 | 0 |  |  | 0 |  |  | my ($wmstate) = @_; | 
| 626 | 0 | 0 | 0 |  |  |  | if (defined $wmstate && defined (my $num = $wmstate{$wmstate})) { | 
| 627 | 0 |  |  |  |  |  | return $num; | 
| 628 |  |  |  |  |  |  | } | 
| 629 | 0 |  |  |  |  |  | return $wmstate; | 
| 630 |  |  |  |  |  |  | } | 
| 631 |  |  |  |  |  |  | } | 
| 632 |  |  |  |  |  |  |  | 
| 633 |  |  |  |  |  |  | { | 
| 634 |  |  |  |  |  |  | # DontCareState==0 no longer ICCCM | 
| 635 |  |  |  |  |  |  | my @wmstate = ('WithdrawnState', # 0 | 
| 636 |  |  |  |  |  |  | 'NormalState',    # 1 | 
| 637 |  |  |  |  |  |  | 'ZoomState',      # 2, no longer ICCCM | 
| 638 |  |  |  |  |  |  | 'IconicState',    # 3 | 
| 639 |  |  |  |  |  |  | 'InactiveState',  # 4, no longer in ICCCM | 
| 640 |  |  |  |  |  |  | ); | 
| 641 |  |  |  |  |  |  | sub _wmstate_interp { | 
| 642 | 0 |  |  | 0 |  |  | my ($X, $num) = @_; | 
| 643 | 0 | 0 | 0 |  |  |  | if ($X->{'do_interp'} && defined (my $str = $wmstate[$num])) { | 
| 644 | 0 |  |  |  |  |  | return $str; | 
| 645 |  |  |  |  |  |  | } | 
| 646 | 0 |  |  |  |  |  | return $num; | 
| 647 |  |  |  |  |  |  | } | 
| 648 |  |  |  |  |  |  | } | 
| 649 |  |  |  |  |  |  |  | 
| 650 |  |  |  |  |  |  | # Maybe through $X->interp() with ... | 
| 651 |  |  |  |  |  |  | # | 
| 652 |  |  |  |  |  |  | # { | 
| 653 |  |  |  |  |  |  | #   # $X->interp('WmState',$num); | 
| 654 |  |  |  |  |  |  | #   # $X->num('WmState',$str); | 
| 655 |  |  |  |  |  |  | #   my %const_arrays | 
| 656 |  |  |  |  |  |  | #     = ( | 
| 657 |  |  |  |  |  |  | #        WmState => ['WithdrawnState', # 0 | 
| 658 |  |  |  |  |  |  | #                    'NormalState',    # 1 | 
| 659 |  |  |  |  |  |  | #                    'ZoomState',      # 2, no longer ICCCM | 
| 660 |  |  |  |  |  |  | #                    'IconicState',    # 3 | 
| 661 |  |  |  |  |  |  | #                    'InactiveState',  # 4, no longer in ICCCM | 
| 662 |  |  |  |  |  |  | #                   ], | 
| 663 |  |  |  |  |  |  | #       ); | 
| 664 |  |  |  |  |  |  | # | 
| 665 |  |  |  |  |  |  | #   my %const_hashes | 
| 666 |  |  |  |  |  |  | #     = (map { $_ => { X11::Protocol::make_num_hash($const_arrays{$_}) } } | 
| 667 |  |  |  |  |  |  | #        keys %const_arrays); | 
| 668 |  |  |  |  |  |  | # | 
| 669 |  |  |  |  |  |  | # | 
| 670 |  |  |  |  |  |  | #   sub ext_const_init { | 
| 671 |  |  |  |  |  |  | #     my ($X) = @_; | 
| 672 |  |  |  |  |  |  | #     unless ($X->{'ext_const'}->{'WmState'}) { | 
| 673 |  |  |  |  |  |  | #       %{$X->{'ext_const'}} = (%{$X->{'ext_const'}}, %const_arrays); | 
| 674 |  |  |  |  |  |  | #       $X->{'ext_const_num'} ||= {}; | 
| 675 |  |  |  |  |  |  | #       %{$X->{'ext_const_num'}} = (%{$X->{'ext_const_num'}}, %const_hashes); | 
| 676 |  |  |  |  |  |  | #     } | 
| 677 |  |  |  |  |  |  | #   } | 
| 678 |  |  |  |  |  |  | # } | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 682 |  |  |  |  |  |  | # WM_STATE | 
| 683 |  |  |  |  |  |  |  | 
| 684 |  |  |  |  |  |  | sub get_wm_state { | 
| 685 | 0 |  |  | 0 | 1 |  | my ($X, $window) = @_; | 
| 686 | 0 |  |  |  |  |  | my $xa_wm_state = $X->atom('WM_STATE'); | 
| 687 | 0 |  |  |  |  |  | my ($value, $type, $format, $bytes_after) | 
| 688 |  |  |  |  |  |  | = $X->GetProperty ($window, | 
| 689 |  |  |  |  |  |  | $xa_wm_state,  # property | 
| 690 |  |  |  |  |  |  | $xa_wm_state,  # type | 
| 691 |  |  |  |  |  |  | 0,             # offset | 
| 692 |  |  |  |  |  |  | 2,             # length, 2 x CARD32 | 
| 693 |  |  |  |  |  |  | 0);            # delete | 
| 694 | 0 | 0 |  |  |  |  | if ($format == 32) { | 
| 695 | 0 |  |  |  |  |  | return unpack_wm_state($X,$value); | 
| 696 |  |  |  |  |  |  | } else { | 
| 697 | 0 |  |  |  |  |  | return; | 
| 698 |  |  |  |  |  |  | } | 
| 699 |  |  |  |  |  |  | } | 
| 700 |  |  |  |  |  |  |  | 
| 701 |  |  |  |  |  |  | sub unpack_wm_state { | 
| 702 | 0 |  |  | 0 | 1 |  | my ($X, $data) = @_; | 
| 703 | 0 |  |  |  |  |  | my ($state, $icon_window) = unpack 'LL', $data; | 
| 704 | 0 |  |  |  |  |  | return (_wmstate_interp($X,$state), _none_interp($X,$icon_window)); | 
| 705 |  |  |  |  |  |  | } | 
| 706 |  |  |  |  |  |  |  | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 709 |  |  |  |  |  |  | # WM_STATE transitions | 
| 710 |  |  |  |  |  |  |  | 
| 711 |  |  |  |  |  |  | # cf /so/xorg/libX11-1.4.0/src/Iconify.c | 
| 712 |  |  |  |  |  |  | # | 
| 713 |  |  |  |  |  |  | sub iconify { | 
| 714 | 0 |  |  | 0 | 1 |  | my ($X, $window, $root) = @_; | 
| 715 |  |  |  |  |  |  | ### iconify(): $window | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | # The icccm spec doesn't seem to say any particular event mask for this | 
| 718 |  |  |  |  |  |  | # ClientMessage, but follow Xlib Iconify.c and send | 
| 719 |  |  |  |  |  |  | # SubstructureRedirect+SubstructureNotify. | 
| 720 |  |  |  |  |  |  | # | 
| 721 | 0 |  |  |  |  |  | _send_event_to_wm ($X, _root_for_window($X,$window,$root), | 
| 722 |  |  |  |  |  |  | name   => 'ClientMessage', | 
| 723 |  |  |  |  |  |  | window => $window, | 
| 724 |  |  |  |  |  |  | type   => $X->atom('WM_CHANGE_STATE'), | 
| 725 |  |  |  |  |  |  | format => 32, | 
| 726 |  |  |  |  |  |  | data   => pack('L5', 3)); # 3=IconicState | 
| 727 |  |  |  |  |  |  | } | 
| 728 |  |  |  |  |  |  |  | 
| 729 |  |  |  |  |  |  | # cf /so/xorg/libX11-1.4.0/src/Withdraw.c | 
| 730 |  |  |  |  |  |  | # | 
| 731 |  |  |  |  |  |  | sub withdraw { | 
| 732 | 0 |  |  | 0 | 1 |  | my ($X, $window, $root) = @_; | 
| 733 |  |  |  |  |  |  | ### withdraw(): $window, $root | 
| 734 | 0 |  |  |  |  |  | $root = _root_for_window($X,$window,$root); # QueryTree before unmap | 
| 735 | 0 |  |  |  |  |  | $X->UnmapWindow ($window); | 
| 736 | 0 |  |  |  |  |  | _send_event_to_wm ($X, $root, | 
| 737 |  |  |  |  |  |  | name   => 'UnmapNotify', | 
| 738 |  |  |  |  |  |  | event  => $root, | 
| 739 |  |  |  |  |  |  | window => $window, | 
| 740 |  |  |  |  |  |  | from_configure => 0); | 
| 741 |  |  |  |  |  |  | } | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  | # =item C<_send_event_to_wm ($X, $root, name=E$str,...)> | 
| 744 |  |  |  |  |  |  | # | 
| 745 |  |  |  |  |  |  | # Send an event to the window manager by C<$X-ESendEvent()> to the given | 
| 746 |  |  |  |  |  |  | # C<$root> (integer XID of a root window). | 
| 747 |  |  |  |  |  |  | # | 
| 748 |  |  |  |  |  |  | # The key/value parameters specify an event packet as per | 
| 749 |  |  |  |  |  |  | # C<$X-Epack_event()>.  Often this is a C event, but any | 
| 750 |  |  |  |  |  |  | # type can be sent.  (For example C sends a synthetic | 
| 751 |  |  |  |  |  |  | # C.) | 
| 752 |  |  |  |  |  |  | # | 
| 753 |  |  |  |  |  |  | # But: event-mask=ColormapChange for own colormap install setups ... | 
| 754 |  |  |  |  |  |  | # But: event-mask=StructureNotify for "manager" acquiring resource ... | 
| 755 |  |  |  |  |  |  | # | 
| 756 |  |  |  |  |  |  | sub _send_event_to_wm { | 
| 757 | 0 |  |  | 0 |  |  | my $X = shift; | 
| 758 | 0 |  |  |  |  |  | my $root = shift; | 
| 759 | 0 |  |  |  |  |  | $X->SendEvent ($root, | 
| 760 |  |  |  |  |  |  | 0,  # all clients | 
| 761 |  |  |  |  |  |  | $X->pack_event_mask('SubstructureRedirect', | 
| 762 |  |  |  |  |  |  | 'SubstructureNotify'), | 
| 763 |  |  |  |  |  |  | $X->pack_event(@_)); | 
| 764 |  |  |  |  |  |  | } | 
| 765 |  |  |  |  |  |  |  | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 768 |  |  |  |  |  |  | # WM_TRANSIENT | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | # $transient_for eq 'None' supported for generality, but not yet documented | 
| 771 |  |  |  |  |  |  | # since not sure such a property value would be ICCCM compliant | 
| 772 |  |  |  |  |  |  | # | 
| 773 |  |  |  |  |  |  | sub set_wm_transient_for { | 
| 774 | 0 |  |  | 0 | 1 |  | my ($X, $window, $transient_for) = @_; | 
| 775 | 0 |  |  |  |  |  | _set_card32_property ($X, $window, | 
| 776 |  |  |  |  |  |  | X11::AtomConstants::WM_TRANSIENT_FOR(),  # prop name | 
| 777 |  |  |  |  |  |  | X11::AtomConstants::WINDOW(),            # type | 
| 778 |  |  |  |  |  |  | _num_none ($transient_for)); | 
| 779 |  |  |  |  |  |  | } | 
| 780 |  |  |  |  |  |  |  | 
| 781 |  |  |  |  |  |  | # not sure about this, might be only used by window manager, not a client | 
| 782 |  |  |  |  |  |  | # =item C<$transient_for = X11::Protocol::WM::get_wm_transient_for ($X, $window)> | 
| 783 |  |  |  |  |  |  | # sub get_wm_transient_for { | 
| 784 |  |  |  |  |  |  | #   my ($X, $window) = @_; | 
| 785 |  |  |  |  |  |  | #   _get_card32_property ($X, $window, | 
| 786 |  |  |  |  |  |  | #                         X11::AtomConstants::WM_TRANSIENT_FOR(), | 
| 787 |  |  |  |  |  |  | #                         X11::AtomConstants::WINDOW()); | 
| 788 |  |  |  |  |  |  | # } | 
| 789 |  |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  |  | 
| 791 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 792 |  |  |  |  |  |  | # _MOTIF_WM_HINTS | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | sub set_motif_wm_hints { | 
| 795 | 0 |  |  | 0 | 1 |  | my $X = shift; | 
| 796 | 0 |  |  |  |  |  | my $window = shift; | 
| 797 | 0 |  |  |  |  |  | $X->ChangeProperty($window, | 
| 798 |  |  |  |  |  |  | $X->atom('_MOTIF_WM_HINTS'), # property | 
| 799 |  |  |  |  |  |  | $X->atom('_MOTIF_WM_HINTS'), # type | 
| 800 |  |  |  |  |  |  | 32,                          # format | 
| 801 |  |  |  |  |  |  | 'Replace', | 
| 802 |  |  |  |  |  |  | pack_motif_wm_hints ($X, @_)); | 
| 803 |  |  |  |  |  |  | } | 
| 804 |  |  |  |  |  |  |  | 
| 805 |  |  |  |  |  |  | { | 
| 806 |  |  |  |  |  |  | # per /usr/include/Xm/MwmUtil.h | 
| 807 |  |  |  |  |  |  | my %key_to_flag = (functions   => 1, | 
| 808 |  |  |  |  |  |  | decorations => 2, | 
| 809 |  |  |  |  |  |  | input_mode  => 4, | 
| 810 |  |  |  |  |  |  | status      => 8, | 
| 811 |  |  |  |  |  |  | ); | 
| 812 |  |  |  |  |  |  | sub pack_motif_wm_hints { | 
| 813 | 0 |  |  | 0 | 0 |  | my ($X, %hint) = @_; | 
| 814 |  |  |  |  |  |  |  | 
| 815 | 0 |  |  |  |  |  | my $flags = 0; | 
| 816 | 0 |  |  |  |  |  | my $key; | 
| 817 | 0 |  |  |  |  |  | foreach $key (keys %hint) { | 
| 818 | 0 | 0 |  |  |  |  | if (defined $hint{$key}) { | 
| 819 | 0 |  |  |  |  |  | $flags |= $key_to_flag{$key}; | 
| 820 |  |  |  |  |  |  | } else { | 
| 821 | 0 |  |  |  |  |  | croak "Unrecognised _MOTIF_WM_HINTS field: ",$key; | 
| 822 |  |  |  |  |  |  | } | 
| 823 |  |  |  |  |  |  | } | 
| 824 |  |  |  |  |  |  | pack ('L5', | 
| 825 |  |  |  |  |  |  | $flags, | 
| 826 |  |  |  |  |  |  | $hint{'functions'} || 0, | 
| 827 |  |  |  |  |  |  | $hint{'decorations'} || 0, | 
| 828 |  |  |  |  |  |  | _motif_input_mode_num($X, $hint{'input_mode'} || 0), | 
| 829 | 0 |  | 0 |  |  |  | $hint{'status'} || 0); | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 830 |  |  |  |  |  |  | } | 
| 831 |  |  |  |  |  |  | } | 
| 832 |  |  |  |  |  |  | { | 
| 833 |  |  |  |  |  |  | my %input_mode_num = (modeless                  => 0, | 
| 834 |  |  |  |  |  |  | primary_application_modal => 1, | 
| 835 |  |  |  |  |  |  | system_modal              => 2, | 
| 836 |  |  |  |  |  |  | full_application_modal    => 3, | 
| 837 |  |  |  |  |  |  |  | 
| 838 |  |  |  |  |  |  | # application_modal         => 1, | 
| 839 |  |  |  |  |  |  | ); | 
| 840 |  |  |  |  |  |  | sub _motif_input_mode_num { | 
| 841 | 0 |  |  | 0 |  |  | my ($X, $input_mode) = @_; | 
| 842 | 0 | 0 |  |  |  |  | if (exists $input_mode_num{$input_mode}) { | 
| 843 | 0 |  |  |  |  |  | return $input_mode_num{$input_mode}; | 
| 844 |  |  |  |  |  |  | } else { | 
| 845 | 0 |  |  |  |  |  | return $input_mode; | 
| 846 |  |  |  |  |  |  | } | 
| 847 |  |  |  |  |  |  | } | 
| 848 |  |  |  |  |  |  | } | 
| 849 |  |  |  |  |  |  |  | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  |  | 
| 852 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 853 |  |  |  |  |  |  | # _NET_FRAME_EXTENTS | 
| 854 |  |  |  |  |  |  |  | 
| 855 |  |  |  |  |  |  | sub get_net_frame_extents { | 
| 856 | 0 |  |  | 0 | 1 |  | my ($X, $window) = @_; | 
| 857 | 0 |  |  |  |  |  | my ($value, $type, $format, $bytes_after) | 
| 858 |  |  |  |  |  |  | = $X->GetProperty ($window, | 
| 859 |  |  |  |  |  |  | $X->atom('_NET_FRAME_EXTENTS'),  # property | 
| 860 |  |  |  |  |  |  | X11::AtomConstants::CARDINAL(),  # type | 
| 861 |  |  |  |  |  |  | 0,    # offset | 
| 862 |  |  |  |  |  |  | 4,    # length, 4 x CARD32 | 
| 863 |  |  |  |  |  |  | 0);   # delete | 
| 864 | 0 | 0 |  |  |  |  | if ($format == 32) { | 
| 865 | 0 |  |  |  |  |  | return unpack 'L4', $value; | 
| 866 |  |  |  |  |  |  | } else { | 
| 867 | 0 |  |  |  |  |  | return; | 
| 868 |  |  |  |  |  |  | } | 
| 869 |  |  |  |  |  |  | } | 
| 870 |  |  |  |  |  |  |  | 
| 871 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 872 |  |  |  |  |  |  | # _NET_WM_PID | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | sub set_net_wm_pid { | 
| 875 | 0 |  |  | 0 | 1 |  | my ($X, $window, $pid) = @_; | 
| 876 | 0 | 0 |  |  |  |  | if (@_ < 3) { $pid = $$; } | 
|  | 0 |  |  |  |  |  |  | 
| 877 | 0 |  |  |  |  |  | _set_card32_property ($X, | 
| 878 |  |  |  |  |  |  | $window, | 
| 879 |  |  |  |  |  |  | $X->atom('_NET_WM_PID'), | 
| 880 |  |  |  |  |  |  | X11::AtomConstants::CARDINAL(), | 
| 881 |  |  |  |  |  |  | $pid); | 
| 882 |  |  |  |  |  |  | } | 
| 883 |  |  |  |  |  |  |  | 
| 884 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 885 |  |  |  |  |  |  | # _NET_WM_STATE | 
| 886 |  |  |  |  |  |  |  | 
| 887 |  |  |  |  |  |  | sub get_net_wm_state { | 
| 888 | 0 |  |  | 0 | 1 |  | my ($X, $window) = @_; | 
| 889 |  |  |  |  |  |  | # ENHANCE-ME: maybe atom_names() for parallel name fetch | 
| 890 | 0 |  |  |  |  |  | return map {_net_wm_state_interp($X,$_)} get_net_wm_state_atoms($X,$window); | 
|  | 0 |  |  |  |  |  |  | 
| 891 |  |  |  |  |  |  | } | 
| 892 |  |  |  |  |  |  | # $atom is an atom integer, return a string like "FULLSCREEN". | 
| 893 |  |  |  |  |  |  | sub _net_wm_state_interp { | 
| 894 | 0 |  |  | 0 |  |  | my ($X, $atom) = @_; | 
| 895 | 0 |  |  |  |  |  | my $state = $X->atom_name($atom); | 
| 896 | 0 |  |  |  |  |  | $state =~ s/^_NET_WM_STATE_//; | 
| 897 | 0 |  |  |  |  |  | return $state; | 
| 898 |  |  |  |  |  |  | } | 
| 899 |  |  |  |  |  |  | sub get_net_wm_state_atoms { | 
| 900 | 0 |  |  | 0 | 1 |  | my ($X, $window) = @_; | 
| 901 | 0 |  |  |  |  |  | my ($value, $type, $format, $bytes_after) | 
| 902 |  |  |  |  |  |  | = $X->GetProperty ($window, | 
| 903 |  |  |  |  |  |  | $X->atom('_NET_WM_STATE'),   # property | 
| 904 |  |  |  |  |  |  | X11::AtomConstants::ATOM(),  # type | 
| 905 |  |  |  |  |  |  | 0,    # offset | 
| 906 |  |  |  |  |  |  | 999,  # length limit | 
| 907 |  |  |  |  |  |  | 0);   # delete | 
| 908 | 0 | 0 |  |  |  |  | if ($format == 32) { | 
| 909 | 0 |  |  |  |  |  | return unpack('L*', $value); | 
| 910 |  |  |  |  |  |  | } else { | 
| 911 | 0 |  |  |  |  |  | return; | 
| 912 |  |  |  |  |  |  | } | 
| 913 |  |  |  |  |  |  | } | 
| 914 |  |  |  |  |  |  |  | 
| 915 |  |  |  |  |  |  | # $state is a string "_NET_WM_STATE_FULLSCREEN" etc, or an integer atom | 
| 916 |  |  |  |  |  |  | # number.  Return an integer atom number. | 
| 917 |  |  |  |  |  |  | sub _net_wm_state_num { | 
| 918 | 0 |  |  | 0 |  |  | my ($X, $state) = @_; | 
| 919 | 0 | 0 |  |  |  |  | if (! defined $state) { | 
| 920 | 0 |  |  |  |  |  | return 0; | 
| 921 |  |  |  |  |  |  | } | 
| 922 | 0 | 0 |  |  |  |  | if ($state =~ /^\d+$/) { | 
| 923 | 0 |  |  |  |  |  | return $state;  # a number already | 
| 924 |  |  |  |  |  |  | } | 
| 925 | 0 | 0 |  |  |  |  | if ($state !~ /^_NET_WM_STATE_/) { | 
| 926 | 0 |  |  |  |  |  | $state = '_NET_WM_STATE_' . $state; | 
| 927 |  |  |  |  |  |  | } | 
| 928 | 0 |  |  |  |  |  | return $X->atom($state); | 
| 929 |  |  |  |  |  |  | } | 
| 930 |  |  |  |  |  |  |  | 
| 931 |  |  |  |  |  |  | sub set_net_wm_state { | 
| 932 | 0 |  |  | 0 | 1 |  | my $X = shift; | 
| 933 | 0 |  |  |  |  |  | my $window = shift; | 
| 934 |  |  |  |  |  |  | $X->ChangeProperty($window, | 
| 935 |  |  |  |  |  |  | $X->atom('_NET_WM_STATE'),   # property | 
| 936 |  |  |  |  |  |  | X11::AtomConstants::ATOM(),  # type | 
| 937 |  |  |  |  |  |  | 32,                          # format | 
| 938 |  |  |  |  |  |  | 'Replace', | 
| 939 | 0 |  |  |  |  |  | pack('L*', map {_net_wm_state_num($X,$_)} @_)); | 
|  | 0 |  |  |  |  |  |  | 
| 940 |  |  |  |  |  |  | } | 
| 941 |  |  |  |  |  |  |  | 
| 942 |  |  |  |  |  |  | { | 
| 943 |  |  |  |  |  |  | my %_net_wm_state_action_num = (remove => 0, | 
| 944 |  |  |  |  |  |  | add    => 1, | 
| 945 |  |  |  |  |  |  | toggle => 2); | 
| 946 |  |  |  |  |  |  | # $action is a string "add" etc, or a number 0,1,2. | 
| 947 |  |  |  |  |  |  | # Return a number 0,1,2. | 
| 948 |  |  |  |  |  |  | sub _net_wm_state_action_num { | 
| 949 | 0 |  |  | 0 |  |  | my ($X, $action) = @_; | 
| 950 |  |  |  |  |  |  | ### _net_wm_state_action_num(): $action | 
| 951 | 0 | 0 |  |  |  |  | if ($action =~ /^\d+$/) { | 
| 952 | 0 |  |  |  |  |  | return $action;  # a number already | 
| 953 |  |  |  |  |  |  | } | 
| 954 | 0 |  |  |  |  |  | my $num = $_net_wm_state_action_num{$action}; | 
| 955 | 0 | 0 |  |  |  |  | if (defined $num) { | 
| 956 | 0 |  |  |  |  |  | return $num; | 
| 957 |  |  |  |  |  |  | } | 
| 958 | 0 |  |  |  |  |  | croak 'Unrecognized _NET_WM_STATE action: ',$action; | 
| 959 |  |  |  |  |  |  | } | 
| 960 |  |  |  |  |  |  | } | 
| 961 |  |  |  |  |  |  |  | 
| 962 |  |  |  |  |  |  | { | 
| 963 |  |  |  |  |  |  | my %_net_wm_source_num = (none   => 0, | 
| 964 |  |  |  |  |  |  | normal => 1, | 
| 965 |  |  |  |  |  |  | user   => 2); | 
| 966 |  |  |  |  |  |  | # $source is a string "normal" etc, or a number 0,1,2. | 
| 967 |  |  |  |  |  |  | # Return a number 0,1,2. | 
| 968 |  |  |  |  |  |  | sub _net_wm_source_num { | 
| 969 | 0 |  |  | 0 |  |  | my ($X, $source) = @_; | 
| 970 | 0 | 0 |  |  |  |  | if (! defined $source) { | 
| 971 | 0 |  |  |  |  |  | return 1;  # default "normal" | 
| 972 |  |  |  |  |  |  | } | 
| 973 | 0 | 0 |  |  |  |  | if ($source =~ /^\d+$/) { | 
| 974 | 0 |  |  |  |  |  | return $source;  # a number already | 
| 975 |  |  |  |  |  |  | } | 
| 976 | 0 |  |  |  |  |  | my $num = $_net_wm_source_num{$source}; | 
| 977 | 0 | 0 |  |  |  |  | if (defined $num) { | 
| 978 | 0 |  |  |  |  |  | return $num; | 
| 979 |  |  |  |  |  |  | } | 
| 980 | 0 |  |  |  |  |  | croak 'Unrecognized _NET_WM source: ',$source; | 
| 981 |  |  |  |  |  |  | } | 
| 982 |  |  |  |  |  |  | } | 
| 983 |  |  |  |  |  |  |  | 
| 984 |  |  |  |  |  |  | sub change_net_wm_state { | 
| 985 | 0 |  |  | 0 | 1 |  | my ($X, $window, $action, $state, %h) = @_; | 
| 986 |  |  |  |  |  |  | ### change_net_wm_state() ... | 
| 987 |  |  |  |  |  |  | ### $state | 
| 988 |  |  |  |  |  |  | ### %h | 
| 989 |  |  |  |  |  |  |  | 
| 990 |  |  |  |  |  |  | my $root = X11::Protocol::WM::_root_for_window($X,$window, | 
| 991 | 0 |  |  |  |  |  | delete $h{'root'}); | 
| 992 | 0 |  |  |  |  |  | my $state2 = _net_wm_state_num($X, delete $h{'state2'}); | 
| 993 | 0 |  |  |  |  |  | my $source = _net_wm_source_num($X, delete $h{'source'}); | 
| 994 | 0 | 0 |  |  |  |  | if (%h) { | 
| 995 | 0 |  |  |  |  |  | croak "change_net_wm_state() unrecognised parameter(s): ", | 
| 996 |  |  |  |  |  |  | join(',',keys %h); | 
| 997 |  |  |  |  |  |  | } | 
| 998 | 0 |  |  |  |  |  | X11::Protocol::WM::_send_event_to_wm ($X, $root, | 
| 999 |  |  |  |  |  |  | name   => 'ClientMessage', | 
| 1000 |  |  |  |  |  |  | window => $window, | 
| 1001 |  |  |  |  |  |  | type   => $X->atom('_NET_WM_STATE'), | 
| 1002 |  |  |  |  |  |  | format => 32, | 
| 1003 |  |  |  |  |  |  | data   => pack('L5', | 
| 1004 |  |  |  |  |  |  | _net_wm_state_action_num($X, $action), | 
| 1005 |  |  |  |  |  |  | _net_wm_state_num($X, $state), | 
| 1006 |  |  |  |  |  |  | $state2, | 
| 1007 |  |  |  |  |  |  | $source)); | 
| 1008 |  |  |  |  |  |  | } | 
| 1009 |  |  |  |  |  |  |  | 
| 1010 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1011 |  |  |  |  |  |  | # _NET_WM_WINDOW_TYPE | 
| 1012 |  |  |  |  |  |  |  | 
| 1013 |  |  |  |  |  |  | sub set_net_wm_window_type { | 
| 1014 | 0 |  |  | 0 | 1 |  | my ($X, $window, $window_type) = @_; | 
| 1015 | 0 |  |  |  |  |  | _set_card32_property ($X, | 
| 1016 |  |  |  |  |  |  | $window, | 
| 1017 |  |  |  |  |  |  | $X->atom('_NET_WM_WINDOW_TYPE'), | 
| 1018 |  |  |  |  |  |  | X11::AtomConstants::ATOM(), | 
| 1019 |  |  |  |  |  |  | _net_wm_window_type_to_atom ($X, $window_type)); | 
| 1020 |  |  |  |  |  |  | } | 
| 1021 |  |  |  |  |  |  |  | 
| 1022 |  |  |  |  |  |  | # not documented yet ... | 
| 1023 |  |  |  |  |  |  | sub _net_wm_window_type_to_atom { | 
| 1024 | 0 |  |  | 0 |  |  | my ($X, $window_type) = @_; | 
| 1025 | 0 | 0 | 0 |  |  |  | if (! defined $window_type || $window_type =~ /^\d+$/) { | 
| 1026 | 0 |  |  |  |  |  | return $window_type; | 
| 1027 |  |  |  |  |  |  | } else { | 
| 1028 | 0 |  |  |  |  |  | return $X->atom ("_NET_WM_WINDOW_TYPE_$window_type"); | 
| 1029 |  |  |  |  |  |  | } | 
| 1030 |  |  |  |  |  |  | } | 
| 1031 |  |  |  |  |  |  |  | 
| 1032 |  |  |  |  |  |  | # unless ($window_type =~ /^_NET_WM/) { | 
| 1033 |  |  |  |  |  |  | # } | 
| 1034 |  |  |  |  |  |  | # my ($akey, $atype) = _atoms ($X, | 
| 1035 |  |  |  |  |  |  | #                              '_NET_WM_WINDOW_TYPE', | 
| 1036 |  |  |  |  |  |  | #                              "_NET_WM_WINDOW_TYPE_$window_type"); | 
| 1037 |  |  |  |  |  |  | #  a type stringcan be an atom integer, a full atom name like | 
| 1038 |  |  |  |  |  |  | # "_NET_WM_WINDOW_TYPE_NORMAL", or just the type part "NORMAL".  The types in | 
| 1039 |  |  |  |  |  |  | # the EWMH spec are | 
| 1040 |  |  |  |  |  |  |  | 
| 1041 |  |  |  |  |  |  |  | 
| 1042 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1043 |  |  |  |  |  |  | # _NET_WM_USER_TIME | 
| 1044 |  |  |  |  |  |  |  | 
| 1045 |  |  |  |  |  |  | sub set_net_wm_user_time { | 
| 1046 | 0 |  |  | 0 | 1 |  | my ($X, $window, $time) = @_; | 
| 1047 | 0 |  |  |  |  |  | _set_card32_property ($X, | 
| 1048 |  |  |  |  |  |  | $window, | 
| 1049 |  |  |  |  |  |  | $X->atom('_NET_WM_USER_TIME'), | 
| 1050 |  |  |  |  |  |  | X11::AtomConstants::CARDINAL(), | 
| 1051 |  |  |  |  |  |  | $time); | 
| 1052 |  |  |  |  |  |  | } | 
| 1053 |  |  |  |  |  |  |  | 
| 1054 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1055 |  |  |  |  |  |  | # WM_NORMAL_HINTS | 
| 1056 |  |  |  |  |  |  |  | 
| 1057 |  |  |  |  |  |  | sub set_wm_normal_hints { | 
| 1058 | 0 |  |  | 0 | 1 |  | my $X = shift; | 
| 1059 | 0 |  |  |  |  |  | my $window = shift; | 
| 1060 | 0 |  |  |  |  |  | $X->ChangeProperty($window, | 
| 1061 |  |  |  |  |  |  | X11::AtomConstants::WM_NORMAL_HINTS(),  # property | 
| 1062 |  |  |  |  |  |  | X11::AtomConstants::WM_SIZE_HINTS(),    # type | 
| 1063 |  |  |  |  |  |  | 32,                                     # format | 
| 1064 |  |  |  |  |  |  | 'Replace', | 
| 1065 |  |  |  |  |  |  | pack_wm_size_hints ($X, @_)); | 
| 1066 |  |  |  |  |  |  | } | 
| 1067 |  |  |  |  |  |  |  | 
| 1068 |  |  |  |  |  |  | { | 
| 1069 |  |  |  |  |  |  | my %key_to_flag = | 
| 1070 |  |  |  |  |  |  | (user_position    => 1,   # user-specified window x,y | 
| 1071 |  |  |  |  |  |  | user_size        => 2,   # user-specified win width,height | 
| 1072 |  |  |  |  |  |  | program_position => 4,   # program-specified window x,y | 
| 1073 |  |  |  |  |  |  | program_size     => 8,   # program-specified win width,height | 
| 1074 |  |  |  |  |  |  | min_width        => 16, | 
| 1075 |  |  |  |  |  |  | min_height       => 16, | 
| 1076 |  |  |  |  |  |  | max_width        => 32, | 
| 1077 |  |  |  |  |  |  | max_height       => 32, | 
| 1078 |  |  |  |  |  |  | width_inc        => 64, | 
| 1079 |  |  |  |  |  |  | height_inc       => 64, | 
| 1080 |  |  |  |  |  |  | min_aspect       => 128, | 
| 1081 |  |  |  |  |  |  | min_aspect_num   => 128, | 
| 1082 |  |  |  |  |  |  | min_aspect_den   => 128, | 
| 1083 |  |  |  |  |  |  | max_aspect       => 128, | 
| 1084 |  |  |  |  |  |  | max_aspect_num   => 128, | 
| 1085 |  |  |  |  |  |  | max_aspect_den   => 128, | 
| 1086 |  |  |  |  |  |  | base_width       => 256, | 
| 1087 |  |  |  |  |  |  | base_height      => 256, | 
| 1088 |  |  |  |  |  |  | win_gravity      => 512, | 
| 1089 |  |  |  |  |  |  | ); | 
| 1090 |  |  |  |  |  |  | sub pack_wm_size_hints { | 
| 1091 | 0 |  |  | 0 | 0 |  | my ($X, %hint) = @_; | 
| 1092 |  |  |  |  |  |  | ### pack_wm_size_hints(): %hint | 
| 1093 |  |  |  |  |  |  |  | 
| 1094 | 0 |  |  |  |  |  | my $flags = 0; | 
| 1095 | 0 |  |  |  |  |  | my $key; | 
| 1096 | 0 |  |  |  |  |  | foreach $key (keys %hint) { | 
| 1097 | 0 | 0 |  |  |  |  | if (defined $hint{$key}) { | 
| 1098 | 0 |  |  |  |  |  | $flags |= $key_to_flag{$key}; | 
| 1099 |  |  |  |  |  |  | } else { | 
| 1100 | 0 |  |  |  |  |  | croak "Unrecognised WM_NORMAL_HINTS field: ",$key; | 
| 1101 |  |  |  |  |  |  | } | 
| 1102 |  |  |  |  |  |  | } | 
| 1103 |  |  |  |  |  |  | pack ('Lx16L13', | 
| 1104 |  |  |  |  |  |  | $flags, | 
| 1105 |  |  |  |  |  |  | $hint{'min_width'} || 0,        # 1 | 
| 1106 |  |  |  |  |  |  | $hint{'min_height'} || 0,       # 2 | 
| 1107 |  |  |  |  |  |  | $hint{'max_width'} || 0,        # 3 | 
| 1108 |  |  |  |  |  |  | $hint{'max_height'} || 0,       # 4 | 
| 1109 |  |  |  |  |  |  | $hint{'width_inc'} || 0,        # 5 | 
| 1110 |  |  |  |  |  |  | $hint{'height_inc'} || 0,       # 6 | 
| 1111 |  |  |  |  |  |  | _aspect (\%hint, 'min'),        # 7,8 | 
| 1112 |  |  |  |  |  |  | _aspect (\%hint, 'max'),        # 9,10 | 
| 1113 |  |  |  |  |  |  | $hint{'base_width'} || 0,       # 11 | 
| 1114 |  |  |  |  |  |  | $hint{'base_height'} || 0,      # 12 | 
| 1115 | 0 |  | 0 |  |  |  | $X->num('WinGravity',$hint{'win_gravity'} || 0),  # 13 | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 1116 |  |  |  |  |  |  | ); | 
| 1117 |  |  |  |  |  |  | } | 
| 1118 |  |  |  |  |  |  | } | 
| 1119 |  |  |  |  |  |  | sub _aspect { | 
| 1120 | 0 |  |  | 0 |  |  | my ($hint, $which) = @_; | 
| 1121 | 0 | 0 |  |  |  |  | if (defined (my $aspect = $hint->{"${which}_aspect"})) { | 
| 1122 | 0 |  |  |  |  |  | return aspect_to_num_den($aspect); | 
| 1123 |  |  |  |  |  |  | } else { | 
| 1124 |  |  |  |  |  |  | return ($hint->{"${which}_aspect_num"} || 0, | 
| 1125 | 0 |  | 0 |  |  |  | $hint->{"${which}_aspect_den"} || 0); | 
|  |  |  | 0 |  |  |  |  | 
| 1126 |  |  |  |  |  |  | } | 
| 1127 |  |  |  |  |  |  | } | 
| 1128 |  |  |  |  |  |  | sub aspect_to_num_den { | 
| 1129 | 0 |  |  | 0 | 1 |  | my ($aspect) = @_; | 
| 1130 |  |  |  |  |  |  | ### $aspect | 
| 1131 |  |  |  |  |  |  |  | 
| 1132 | 0 |  |  |  |  |  | my ($num, $den); | 
| 1133 |  |  |  |  |  |  |  | 
| 1134 | 0 | 0 |  |  |  |  | if ($aspect =~ /^\d+$/) { | 
|  |  | 0 |  |  |  |  |  | 
| 1135 |  |  |  |  |  |  | ### integer | 
| 1136 | 0 |  |  |  |  |  | $num = $aspect; | 
| 1137 | 0 |  |  |  |  |  | $den = 1; | 
| 1138 |  |  |  |  |  |  | } elsif (($num,$den) = ($aspect =~ m{(.*)/(.*)})) { | 
| 1139 |  |  |  |  |  |  | ### slash fraction | 
| 1140 |  |  |  |  |  |  | } else { | 
| 1141 | 0 |  |  |  |  |  | $num = $aspect; | 
| 1142 | 0 |  |  |  |  |  | $den = 1; | 
| 1143 |  |  |  |  |  |  | } | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 | 0 |  |  |  |  |  | my $den_zeros = 0; | 
| 1146 | 0 | 0 |  |  |  |  | if ($num =~ /^0*(\d*)\.(\d*?)0*$/) { | 
| 1147 |  |  |  |  |  |  | ### decimal | 
| 1148 | 0 |  |  |  |  |  | $num = "$1$2"; | 
| 1149 | 0 |  |  |  |  |  | $den_zeros = length($2); | 
| 1150 |  |  |  |  |  |  | } | 
| 1151 | 0 | 0 |  |  |  |  | if ($den =~ /^0*(\d*)\.(\d*?)0*$/) { | 
| 1152 |  |  |  |  |  |  | ### decimal | 
| 1153 | 0 |  |  |  |  |  | $den = "$1$2"; | 
| 1154 | 0 |  |  |  |  |  | $den_zeros -= length($2); | 
| 1155 |  |  |  |  |  |  | } | 
| 1156 | 0 | 0 |  |  |  |  | if ($den_zeros > 0) { | 
| 1157 | 0 |  |  |  |  |  | $den .= '0' x $den_zeros; | 
| 1158 |  |  |  |  |  |  | } | 
| 1159 | 0 | 0 |  |  |  |  | if ($den_zeros < 0) { | 
| 1160 | 0 |  |  |  |  |  | $num .= '0' x -$den_zeros; | 
| 1161 |  |  |  |  |  |  | } | 
| 1162 |  |  |  |  |  |  |  | 
| 1163 | 0 | 0 |  |  |  |  | if ($num == $num-1) {  # infinity | 
| 1164 | 0 | 0 |  |  |  |  | return (0x7FFF_FFFF, ($den == $den-1  # infinity too | 
| 1165 |  |  |  |  |  |  | ? 0x7FFF_FFFF : 1)); | 
| 1166 |  |  |  |  |  |  | } | 
| 1167 | 0 | 0 |  |  |  |  | if ($den == $den-1) {  # infinity | 
| 1168 | 0 |  |  |  |  |  | return (1, 0x7FFF_FFFF); | 
| 1169 |  |  |  |  |  |  | } | 
| 1170 |  |  |  |  |  |  |  | 
| 1171 |  |  |  |  |  |  | # cap anything bigger than 0x7FFFFFFF | 
| 1172 | 0 | 0 | 0 |  |  |  | if ($num >= $den && $num > 0x7FFF_FFFF) { | 
| 1173 |  |  |  |  |  |  | ### reduce big numerator | 
| 1174 | 0 |  |  |  |  |  | ($num,$den) = _aspect_reduce($num,$den); | 
| 1175 |  |  |  |  |  |  | } | 
| 1176 | 0 | 0 |  |  |  |  | if ($den > 0x7FFF_FFFF) { | 
| 1177 |  |  |  |  |  |  | ### reduce big denominator | 
| 1178 | 0 |  |  |  |  |  | ($den,$num) = _aspect_reduce($den,$num); | 
| 1179 |  |  |  |  |  |  | } | 
| 1180 |  |  |  |  |  |  |  | 
| 1181 |  |  |  |  |  |  | # increase non-integers in binary | 
| 1182 | 0 |  | 0 |  |  |  | while ((int($num) != $num || int($den) != $den) | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 1183 |  |  |  |  |  |  | && $num < 0x4000_0000 | 
| 1184 |  |  |  |  |  |  | && $den < 0x4000_0000) { | 
| 1185 | 0 |  |  |  |  |  | $num *= 2; | 
| 1186 | 0 |  |  |  |  |  | $den *= 2; | 
| 1187 |  |  |  |  |  |  | ### up to: $num,$den | 
| 1188 |  |  |  |  |  |  | } | 
| 1189 |  |  |  |  |  |  |  | 
| 1190 | 0 |  |  |  |  |  | return (_round_nz($num), _round_nz($den)); | 
| 1191 |  |  |  |  |  |  | } | 
| 1192 |  |  |  |  |  |  |  | 
| 1193 |  |  |  |  |  |  | # Return $x rounded to the nearest integer. | 
| 1194 |  |  |  |  |  |  | # If $x is not zero then the return is not zero too, ie. $x<0.5 is rounded | 
| 1195 |  |  |  |  |  |  | # up to return 1. | 
| 1196 |  |  |  |  |  |  | sub _round_nz { | 
| 1197 | 0 |  |  | 0 |  |  | my ($x) = @_; | 
| 1198 | 0 |  |  |  |  |  | my $nz = ($x != 0); | 
| 1199 | 0 |  |  |  |  |  | $x = int ($x + 0.5); | 
| 1200 | 0 | 0 | 0 |  |  |  | if ($nz && $x == 0) { | 
| 1201 | 0 |  |  |  |  |  | return 1; | 
| 1202 |  |  |  |  |  |  | } else { | 
| 1203 | 0 |  |  |  |  |  | return $x; | 
| 1204 |  |  |  |  |  |  | } | 
| 1205 |  |  |  |  |  |  | } | 
| 1206 |  |  |  |  |  |  |  | 
| 1207 |  |  |  |  |  |  | # $x is > 0x7FFF_FFFF.  Reduce it to 0x7FFF_FFFF and reduce $y in proportion. | 
| 1208 |  |  |  |  |  |  | # If $y!=0 then it's reduced to a minimum 1, not to 0. | 
| 1209 |  |  |  |  |  |  | sub _aspect_reduce { | 
| 1210 | 0 |  |  | 0 |  |  | my ($x,$y) = @_; | 
| 1211 | 0 |  |  |  |  |  | my $nz = ($y != 0); | 
| 1212 | 0 |  |  |  |  |  | $y = int (0.5 + $y / $x * 0x7FFF_FFFF); | 
| 1213 | 0 | 0 | 0 |  |  |  | if ($nz && $y == 0) { $y = 1; } | 
|  | 0 | 0 |  |  |  |  |  | 
| 1214 | 0 |  |  |  |  |  | elsif ($y > 0x7FFF_FFFF) { $y = 0x7FFF_FFFF; } | 
| 1215 | 0 |  |  |  |  |  | return (0x7FFF_FFFF, $y); | 
| 1216 |  |  |  |  |  |  | } | 
| 1217 |  |  |  |  |  |  | # printf "%d %d", _aspect_frac('.123456789'); | 
| 1218 |  |  |  |  |  |  |  | 
| 1219 |  |  |  |  |  |  |  | 
| 1220 |  |  |  |  |  |  |  | 
| 1221 |  |  |  |  |  |  | 1; | 
| 1222 |  |  |  |  |  |  | __END__ |