ruby-wmctrl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *~
6
+ ext/*.so
7
+ ext/*.o
8
+ ext/Makefile
9
+ ext/mkmf.log
data/COPYING ADDED
@@ -0,0 +1,340 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Library General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License
307
+ along with this program; if not, write to the Free Software
308
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
+
310
+
311
+ Also add information on how to contact you by electronic and paper mail.
312
+
313
+ If the program is interactive, make it output a short notice like this
314
+ when it starts in an interactive mode:
315
+
316
+ Gnomovision version 69, Copyright (C) year name of author
317
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318
+ This is free software, and you are welcome to redistribute it
319
+ under certain conditions; type `show c' for details.
320
+
321
+ The hypothetical commands `show w' and `show c' should show the appropriate
322
+ parts of the General Public License. Of course, the commands you use may
323
+ be called something other than `show w' and `show c'; they could even be
324
+ mouse-clicks or menu items--whatever suits your program.
325
+
326
+ You should also get your employer (if you work as a programmer) or your
327
+ school, if any, to sign a "copyright disclaimer" for the program, if
328
+ necessary. Here is a sample; alter the names:
329
+
330
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
+
333
+ <signature of Ty Coon>, 1 April 1989
334
+ Ty Coon, President of Vice
335
+
336
+ This General Public License does not permit incorporating your program into
337
+ proprietary programs. If your program is a subroutine library, you may
338
+ consider it more useful to permit linking proprietary applications with the
339
+ library. If this is what you want to do, use the GNU Library General
340
+ Public License instead of this License.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ruby-wmctrl.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/ext/extconf.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'mkmf'
2
+ require 'pkg-config'
3
+
4
+ $CFLAGS << " -Wall"
5
+
6
+ glib = PKGConfig.package_config("glib-2.0")
7
+ $CFLAGS << " " << glib.cflags
8
+ $libs << " " << glib.libs
9
+
10
+ dir_config("X11")
11
+ if have_header('X11/Xlib.h') && have_library('X11')
12
+ create_makefile("wmctrl")
13
+ end
data/ext/wmctrl.c ADDED
@@ -0,0 +1,1125 @@
1
+ #include <ruby.h>
2
+ #include <stdlib.h>
3
+ #include <unistd.h>
4
+ #include <stdio.h>
5
+ #include <string.h>
6
+ #include <locale.h>
7
+ #include <X11/Xlib.h>
8
+ #include <X11/Xatom.h>
9
+ #include <X11/cursorfont.h>
10
+ #include <X11/Xmu/WinUtil.h>
11
+ #include <glib.h>
12
+
13
+ #include <ruby/encoding.h>
14
+ #define RB_UTF8_STRING_NEW(ptr, size) rb_enc_str_new(ptr, size, rb_utf8_encoding())
15
+ #define RB_UTF8_STRING_NEW2(ptr) rb_enc_str_new(ptr, strlen(ptr), rb_utf8_encoding())
16
+
17
+ #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
18
+ #define _NET_WM_STATE_ADD 1 /* add/set property */
19
+ #define _NET_WM_STATE_TOGGLE 2 /* toggle property */
20
+
21
+ #define MAX_PROPERTY_VALUE_LEN 4096
22
+
23
+ #define p_verbose(...) if (RTEST(ruby_debug)) { \
24
+ fprintf(stderr, __VA_ARGS__); \
25
+ }
26
+
27
+ #define envir_utf8 TRUE
28
+
29
+ static VALUE rb_wmctrl_class, key_id, key_title, key_pid, key_geometry,
30
+ key_class, key_client_machine, key_desktop,
31
+ key_viewport, key_workarea, key_current, key_showing_desktop, key_name;
32
+
33
+ static ID id_select, id_active, id_activate, id_close, id_move_resize,
34
+ id_change_state, id_move_to_desktop, id_move_to_current,
35
+ id_set_title_long, id_set_title_short, id_set_title_both;
36
+
37
+ static void rb_wmctrl_free (void **ptr)
38
+ {
39
+ XCloseDisplay(*ptr);
40
+ free(ptr);
41
+ }
42
+
43
+ static VALUE rb_wmctrl_alloc(VALUE self)
44
+ {
45
+ Display **ptr;
46
+ return Data_Make_Struct(rb_wmctrl_class, Display*, 0, rb_wmctrl_free, ptr);
47
+ }
48
+
49
+ static VALUE rb_wmctrl_initialize(int argc, VALUE *argv, VALUE self)
50
+ {
51
+ Display **ptr;
52
+ Data_Get_Struct(self, Display*, ptr);
53
+ if (! (*ptr = XOpenDisplay(NULL))) {
54
+ rb_raise(rb_eStandardError, "Cannot open display.\n");
55
+ }
56
+ return self;
57
+ }
58
+
59
+ static int client_msg(Display *disp, Window win, const char *msg,
60
+ unsigned long data0, unsigned long data1,
61
+ unsigned long data2, unsigned long data3,
62
+ unsigned long data4) {
63
+ XEvent event;
64
+ long mask = SubstructureRedirectMask | SubstructureNotifyMask;
65
+
66
+ event.xclient.type = ClientMessage;
67
+ event.xclient.serial = 0;
68
+ event.xclient.send_event = True;
69
+ event.xclient.message_type = XInternAtom(disp, msg, False);
70
+ event.xclient.window = win;
71
+ event.xclient.format = 32;
72
+ event.xclient.data.l[0] = data0;
73
+ event.xclient.data.l[1] = data1;
74
+ event.xclient.data.l[2] = data2;
75
+ event.xclient.data.l[3] = data3;
76
+ event.xclient.data.l[4] = data4;
77
+
78
+ if (!XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event)) {
79
+ rb_raise(rb_eStandardError, "Cannot send %s event.\n", msg);
80
+ }
81
+ XFlush(disp); /* Maybe, we need XFlush. */
82
+ return True;
83
+ }
84
+
85
+ /* Copy from debian package for 64bit support. */
86
+ static gchar *get_property (Display *disp, Window win,
87
+ Atom xa_prop_type, const gchar *prop_name, unsigned long *size) {
88
+ Atom xa_prop_name;
89
+ Atom xa_ret_type;
90
+ int ret_format;
91
+ unsigned long ret_nitems;
92
+ unsigned long ret_bytes_after;
93
+ unsigned long tmp_size;
94
+ unsigned char *ret_prop;
95
+ gchar *ret;
96
+
97
+ xa_prop_name = XInternAtom(disp, prop_name, False);
98
+
99
+ /* MAX_PROPERTY_VALUE_LEN / 4 explanation (XGetWindowProperty manpage):
100
+ *
101
+ * long_length = Specifies the length in 32-bit multiples of the
102
+ * data to be retrieved.
103
+ *
104
+ * NOTE: see
105
+ * http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html
106
+ * In particular:
107
+ *
108
+ * When the X window system was ported to 64-bit architectures, a
109
+ * rather peculiar design decision was made. 32-bit quantities such
110
+ * as Window IDs, atoms, etc, were kept as longs in the client side
111
+ * APIs, even when long was changed to 64 bits.
112
+ *
113
+ */
114
+ if (XGetWindowProperty(disp, win, xa_prop_name, 0, MAX_PROPERTY_VALUE_LEN / 4, False,
115
+ xa_prop_type, &xa_ret_type, &ret_format,
116
+ &ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
117
+ p_verbose("Cannot get %s property.\n", prop_name);
118
+ return NULL;
119
+ }
120
+
121
+ if (xa_ret_type != xa_prop_type) {
122
+ p_verbose("Invalid type of %s property.\n", prop_name);
123
+ XFree(ret_prop);
124
+ return NULL;
125
+ }
126
+
127
+ /* null terminate the result to make string handling easier */
128
+ tmp_size = (ret_format / 8) * ret_nitems;
129
+ /* Correct 64 Architecture implementation of 32 bit data */
130
+ if(ret_format==32) tmp_size *= sizeof(long)/4;
131
+ ret = g_malloc(tmp_size + 1);
132
+ memcpy(ret, ret_prop, tmp_size);
133
+ ret[tmp_size] = '\0';
134
+
135
+ if (size) {
136
+ *size = tmp_size;
137
+ }
138
+
139
+ XFree(ret_prop);
140
+ return ret;
141
+ }
142
+
143
+ static Window *get_client_list (Display *disp, unsigned long *size) {
144
+ Window *client_list;
145
+
146
+ if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp),
147
+ XA_WINDOW, "_NET_CLIENT_LIST", size)) == NULL) {
148
+ if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp),
149
+ XA_CARDINAL, "_WIN_CLIENT_LIST", size)) == NULL) {
150
+ fputs("Cannot get client list properties. \n"
151
+ "(_NET_CLIENT_LIST or _WIN_CLIENT_LIST)"
152
+ "\n", stderr);
153
+ return NULL;
154
+ }
155
+ }
156
+
157
+ return client_list;
158
+ }
159
+
160
+ static gchar *get_window_class (Display *disp, Window win) {
161
+ gchar *class_utf8;
162
+ gchar *wm_class;
163
+ unsigned long size;
164
+
165
+ wm_class = get_property(disp, win, XA_STRING, "WM_CLASS", &size);
166
+ if (wm_class) {
167
+ gchar *p_0 = strchr(wm_class, '\0');
168
+ if (wm_class + size - 1 > p_0) {
169
+ *(p_0) = '.';
170
+ }
171
+ class_utf8 = g_locale_to_utf8(wm_class, -1, NULL, NULL, NULL);
172
+ }
173
+ else {
174
+ class_utf8 = NULL;
175
+ }
176
+
177
+ g_free(wm_class);
178
+
179
+ return class_utf8;
180
+ }
181
+
182
+ static gchar *get_window_title (Display *disp, Window win) {
183
+ gchar *title_utf8;
184
+ gchar *wm_name;
185
+ gchar *net_wm_name;
186
+
187
+ wm_name = get_property(disp, win, XA_STRING, "WM_NAME", NULL);
188
+ net_wm_name = get_property(disp, win,
189
+ XInternAtom(disp, "UTF8_STRING", False), "_NET_WM_NAME", NULL);
190
+
191
+ if (net_wm_name) {
192
+ title_utf8 = g_strdup(net_wm_name);
193
+ }
194
+ else {
195
+ if (wm_name) {
196
+ title_utf8 = g_locale_to_utf8(wm_name, -1, NULL, NULL, NULL);
197
+ }
198
+ else {
199
+ title_utf8 = NULL;
200
+ }
201
+ }
202
+
203
+ g_free(wm_name);
204
+ g_free(net_wm_name);
205
+
206
+ return title_utf8;
207
+ }
208
+
209
+ static VALUE rb_wmctrl_list_windows (VALUE self) {
210
+ Display **ptr, *disp;
211
+ Window *client_list;
212
+ unsigned long client_list_size;
213
+ unsigned int i;
214
+ VALUE window_ary;
215
+
216
+ Data_Get_Struct(self, Display*, ptr);
217
+ disp = *ptr;
218
+
219
+ if ((client_list = get_client_list(disp, &client_list_size)) == NULL) {
220
+ /* return EXIT_FAILURE; */
221
+ return Qfalse;
222
+ }
223
+
224
+ window_ary = rb_ary_new2(client_list_size);
225
+
226
+ for (i = 0; i < client_list_size / sizeof(Window); i++) {
227
+ VALUE window_obj = rb_hash_new();
228
+ gchar *title_utf8 = get_window_title(disp, client_list[i]); /* UTF8 */
229
+ gchar *client_machine;
230
+ gchar *class_out = get_window_class(disp, client_list[i]); /* UTF8 */
231
+ unsigned long *pid;
232
+ unsigned long *desktop;
233
+ int x, y, junkx, junky;
234
+ unsigned int wwidth, wheight, bw, depth;
235
+ Window junkroot;
236
+
237
+ /* Use strings of hexadecimal number? At the present we use simply integers. */
238
+ /* printf("0x%.8lx", client_list[i]); */
239
+ rb_hash_aset(window_obj, key_id, INT2NUM(client_list[i]));
240
+ rb_hash_aset(window_obj, key_title, (title_utf8 ? RB_UTF8_STRING_NEW2(title_utf8) : Qnil));
241
+ rb_hash_aset(window_obj, key_class, (class_out ? RB_UTF8_STRING_NEW2(class_out) : Qnil));
242
+
243
+ /* desktop ID */
244
+ if ((desktop = (unsigned long *)get_property(disp, client_list[i],
245
+ XA_CARDINAL, "_NET_WM_DESKTOP", NULL)) == NULL) {
246
+ desktop = (unsigned long *)get_property(disp, client_list[i], XA_CARDINAL, "_WIN_WORKSPACE", NULL);
247
+ }
248
+ /* special desktop ID -1 means "all desktops", so we
249
+ have to convert the desktop value to signed long */
250
+ rb_hash_aset(window_obj, key_desktop, INT2NUM(desktop ? (signed long)*desktop : 0));
251
+
252
+ /* client machine */
253
+ client_machine = get_property(disp, client_list[i], XA_STRING, "WM_CLIENT_MACHINE", NULL);
254
+ rb_hash_aset(window_obj, key_client_machine, (client_machine ? RB_UTF8_STRING_NEW2(client_machine) : Qnil));
255
+
256
+ /* pid */
257
+ pid = (unsigned long *)get_property(disp, client_list[i], XA_CARDINAL, "_NET_WM_PID", NULL);
258
+ rb_hash_aset(window_obj, key_pid, (pid ? ULONG2NUM(*pid) : Qnil));
259
+
260
+ /* geometry */
261
+ XGetGeometry (disp, client_list[i], &junkroot, &junkx, &junky, &wwidth, &wheight, &bw, &depth);
262
+ XTranslateCoordinates (disp, client_list[i], junkroot, junkx, junky, &x, &y, &junkroot);
263
+
264
+ rb_hash_aset(window_obj, key_geometry,
265
+ rb_ary_new3(4, INT2NUM(x), INT2NUM(y), INT2NUM(wwidth), INT2NUM(wheight)));
266
+
267
+ rb_ary_push(window_ary, window_obj);
268
+
269
+ g_free(title_utf8);
270
+ g_free(desktop);
271
+ g_free(client_machine);
272
+ g_free(class_out);
273
+ g_free(pid);
274
+ }
275
+ g_free(client_list);
276
+
277
+ return window_ary;
278
+ }
279
+
280
+ static VALUE rb_wmctrl_list_desktops (VALUE self) {
281
+ Display **ptr, *disp;
282
+ unsigned long *num_desktops = NULL;
283
+ unsigned long *cur_desktop = NULL;
284
+ unsigned long desktop_list_size = 0;
285
+ unsigned long *desktop_geometry = NULL;
286
+ unsigned long desktop_geometry_size = 0;
287
+ unsigned long *desktop_viewport = NULL;
288
+ unsigned long desktop_viewport_size = 0;
289
+ unsigned long *desktop_workarea = NULL;
290
+ unsigned long desktop_workarea_size = 0;
291
+ gchar *list = NULL;
292
+ unsigned int i;
293
+ unsigned int id;
294
+ Window root;
295
+ const gchar *error_message = NULL;
296
+ VALUE ret = Qnil;
297
+ VALUE *ret_arys;
298
+
299
+ Data_Get_Struct(self, Display*, ptr);
300
+ disp = *ptr;
301
+
302
+ root = DefaultRootWindow(disp);
303
+
304
+ if (! (num_desktops = (unsigned long *)get_property(disp, root,
305
+ XA_CARDINAL, "_NET_NUMBER_OF_DESKTOPS", NULL))) {
306
+ if (! (num_desktops = (unsigned long *)get_property(disp, root,
307
+ XA_CARDINAL, "_WIN_WORKSPACE_COUNT", NULL))) {
308
+ error_message = "Cannot get number of desktops properties. (_NET_NUMBER_OF_DESKTOPS or _WIN_WORKSPACE_COUNT)";
309
+ goto cleanup;
310
+ }
311
+ }
312
+
313
+ if (! (cur_desktop = (unsigned long *)get_property(disp, root,
314
+ XA_CARDINAL, "_NET_CURRENT_DESKTOP", NULL))) {
315
+ if (! (cur_desktop = (unsigned long *)get_property(disp, root,
316
+ XA_CARDINAL, "_WIN_WORKSPACE", NULL))) {
317
+ error_message = "Cannot get current desktop properties. (_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)";
318
+ goto cleanup;
319
+ }
320
+ }
321
+
322
+ if ((list = get_property(disp, root, XInternAtom(disp, "UTF8_STRING", False),
323
+ "_NET_DESKTOP_NAMES", &desktop_list_size)) == NULL) {
324
+ /* If charaset is not utf8 then there may be bugs here. */
325
+ if ((list = get_property(disp, root,
326
+ XA_STRING,
327
+ "_WIN_WORKSPACE_NAMES", &desktop_list_size)) == NULL) {
328
+ p_verbose("Cannot get desktop names properties. "
329
+ "(_NET_DESKTOP_NAMES or _WIN_WORKSPACE_NAMES)"
330
+ "\n");
331
+ /* ignore the error - list the desktops without names */
332
+ }
333
+ }
334
+
335
+ /* common size of all desktops */
336
+ if (! (desktop_geometry = (unsigned long *)get_property(disp, DefaultRootWindow(disp),
337
+ XA_CARDINAL, "_NET_DESKTOP_GEOMETRY", &desktop_geometry_size))) {
338
+ p_verbose("Cannot get common size of all desktops (_NET_DESKTOP_GEOMETRY).\n");
339
+ }
340
+
341
+ /* desktop viewport */
342
+ if (! (desktop_viewport = (unsigned long *)get_property(disp, DefaultRootWindow(disp),
343
+ XA_CARDINAL, "_NET_DESKTOP_VIEWPORT", &desktop_viewport_size))) {
344
+ p_verbose("Cannot get common size of all desktops (_NET_DESKTOP_VIEWPORT).\n");
345
+ }
346
+
347
+ /* desktop workarea */
348
+ if (! (desktop_workarea = (unsigned long *)get_property(disp, DefaultRootWindow(disp),
349
+ XA_CARDINAL, "_NET_WORKAREA", &desktop_workarea_size))) {
350
+ if (! (desktop_workarea = (unsigned long *)get_property(disp, DefaultRootWindow(disp),
351
+ XA_CARDINAL, "_WIN_WORKAREA", &desktop_workarea_size))) {
352
+ p_verbose("Cannot get _NET_WORKAREA property.\n");
353
+ }
354
+ }
355
+
356
+ ret_arys = (VALUE *)g_malloc0(*num_desktops * sizeof(VALUE));
357
+ for (i = 0; i < *num_desktops; i++) {
358
+ ret_arys[i] = rb_hash_new();
359
+ rb_hash_aset(ret_arys[i], key_id, UINT2NUM(i));
360
+ if (i == *cur_desktop) {
361
+ rb_hash_aset(ret_arys[i], key_current, Qtrue);
362
+ } else {
363
+ rb_hash_aset(ret_arys[i], key_current, Qnil);
364
+ }
365
+ }
366
+
367
+ if (list) {
368
+ id = 0;
369
+ rb_hash_aset(ret_arys[id], key_title, RB_UTF8_STRING_NEW2(list));
370
+ id++;
371
+ for (i = 0; i < desktop_list_size; i++) {
372
+ if (list[i] == '\0') {
373
+ if (id >= *num_desktops) {
374
+ break;
375
+ }
376
+ rb_hash_aset(ret_arys[id], key_title, RB_UTF8_STRING_NEW2(list + i + 1));
377
+ id++;
378
+ }
379
+ }
380
+ }
381
+
382
+ /* prepare desktop geometry strings */
383
+ if (desktop_geometry && desktop_geometry_size > 0) {
384
+ if (desktop_geometry_size == 2 * sizeof(*desktop_geometry)) {
385
+ /* only one value - use it for all desktops */
386
+ p_verbose("WM provides _NET_DESKTOP_GEOMETRY value common for all desktops.\n");
387
+ for (i = 0; i < *num_desktops; i++) {
388
+ rb_hash_aset(ret_arys[i], key_geometry,
389
+ rb_ary_new3(2, INT2NUM(desktop_geometry[0]), INT2NUM(desktop_geometry[1])));
390
+ }
391
+ }
392
+ else {
393
+ /* seperate values for desktops of different size */
394
+ p_verbose("WM provides separate _NET_DESKTOP_GEOMETRY value for each desktop.\n");
395
+ for (i = 0; i < *num_desktops; i++) {
396
+ if (i < desktop_geometry_size / sizeof(*desktop_geometry) / 2) {
397
+ rb_hash_aset(ret_arys[i], key_geometry,
398
+ rb_ary_new3(2, INT2NUM(desktop_geometry[i*2]), INT2NUM(desktop_geometry[i*2+1])));
399
+ }
400
+ else {
401
+ rb_hash_aset(ret_arys[i], key_geometry, Qnil);
402
+ }
403
+ }
404
+ }
405
+ }
406
+ else {
407
+ for (i = 0; i < *num_desktops; i++) {
408
+ rb_hash_aset(ret_arys[i], key_geometry, Qnil);
409
+ }
410
+ }
411
+
412
+ /* prepare desktop viewport strings */
413
+ if (desktop_viewport && desktop_viewport_size > 0) {
414
+ if (desktop_viewport_size == 2 * sizeof(*desktop_viewport)) {
415
+ /* only one value - use it for current desktop */
416
+ p_verbose("WM provides _NET_DESKTOP_VIEWPORT value only for the current desktop.\n");
417
+ for (i = 0; i < *num_desktops; i++) {
418
+ if (i == *cur_desktop) {
419
+ rb_hash_aset(ret_arys[i], key_viewport,
420
+ rb_ary_new3(2, INT2NUM(desktop_viewport[0]), INT2NUM(desktop_viewport[1])));
421
+ }
422
+ else {
423
+ rb_hash_aset(ret_arys[i], key_viewport, Qnil);
424
+ }
425
+ }
426
+ }
427
+ else {
428
+ /* seperate values for each of desktops */
429
+ for (i = 0; i < *num_desktops; i++) {
430
+ if (i < desktop_viewport_size / sizeof(*desktop_viewport) / 2) {
431
+ rb_hash_aset(ret_arys[i], key_viewport,
432
+ rb_ary_new3(2, INT2NUM(desktop_viewport[i*2]), INT2NUM(desktop_viewport[i*2+1])));
433
+ }
434
+ else {
435
+ rb_hash_aset(ret_arys[i], key_viewport, Qnil);
436
+ }
437
+ }
438
+ }
439
+ }
440
+ else {
441
+ for (i = 0; i < *num_desktops; i++) {
442
+ rb_hash_aset(ret_arys[i], key_viewport, Qnil);
443
+ }
444
+ }
445
+
446
+ /* prepare desktop workarea strings */
447
+ if (desktop_workarea && desktop_workarea_size > 0) {
448
+ if (desktop_workarea_size == 4 * sizeof(*desktop_workarea)) {
449
+ /* only one value - use it for current desktop */
450
+ p_verbose("WM provides _NET_WORKAREA value only for the current desktop.\n");
451
+ for (i = 0; i < *num_desktops; i++) {
452
+ if (i == *cur_desktop) {
453
+ rb_hash_aset(ret_arys[i], key_workarea,
454
+ rb_ary_new3(4, INT2NUM(desktop_workarea[0]), INT2NUM(desktop_workarea[1]),
455
+ INT2NUM(desktop_workarea[2]), INT2NUM(desktop_workarea[3])));
456
+ }
457
+ else {
458
+ rb_hash_aset(ret_arys[i], key_workarea, Qnil);
459
+ }
460
+ }
461
+ }
462
+ else {
463
+ /* seperate values for each of desktops */
464
+ for (i = 0; i < *num_desktops; i++) {
465
+ if (i < desktop_workarea_size / sizeof(*desktop_workarea) / 4) {
466
+ rb_hash_aset(ret_arys[i], key_workarea,
467
+ rb_ary_new3(4, INT2NUM(desktop_workarea[i*4]), INT2NUM(desktop_workarea[i*4+1]),
468
+ INT2NUM(desktop_workarea[i*4+2]), INT2NUM(desktop_workarea[i*4+3])));
469
+ }
470
+ else {
471
+ rb_hash_aset(ret_arys[i], key_workarea, Qnil);
472
+ }
473
+ }
474
+ }
475
+ }
476
+ else {
477
+ for (i = 0; i < *num_desktops; i++) {
478
+ rb_hash_aset(ret_arys[i], key_workarea, Qnil);
479
+ }
480
+ }
481
+
482
+ ret = rb_ary_new4(*num_desktops, ret_arys);
483
+
484
+ p_verbose("Total number of desktops: %lu\n", *num_desktops);
485
+ p_verbose("Current desktop ID (counted from zero): %lu\n", *cur_desktop);
486
+ goto cleanup;
487
+
488
+ cleanup:
489
+ g_free(num_desktops);
490
+ g_free(cur_desktop);
491
+ g_free(desktop_geometry);
492
+ g_free(desktop_viewport);
493
+ g_free(desktop_workarea);
494
+ g_free(list);
495
+ if (error_message) {
496
+ rb_raise(rb_eStandardError, "%s", error_message);
497
+ }
498
+ return ret;
499
+ }
500
+
501
+ static VALUE rb_wmctrl_switch_desktop (VALUE self, VALUE desktop_id) {
502
+ int target;
503
+ Display **ptr, *disp;
504
+ Data_Get_Struct(self, Display*, ptr);
505
+ disp = *ptr;
506
+
507
+ target = FIX2INT(desktop_id);
508
+ if (target < 0) {
509
+ rb_raise(rb_eStandardError, "Invalid desktop ID: %d", target);
510
+ }
511
+ client_msg(disp, DefaultRootWindow(disp), "_NET_CURRENT_DESKTOP", (unsigned long)target, 0, 0, 0, 0);
512
+ return Qtrue;
513
+ }
514
+
515
+ static VALUE rb_wmctrl_info (VALUE self) {
516
+ Display **ptr, *disp;
517
+ Window *sup_window = NULL;
518
+ gchar *wm_name = NULL;
519
+ gchar *wm_class = NULL;
520
+ unsigned long *wm_pid = NULL;
521
+ unsigned long *showing_desktop = NULL;
522
+ gboolean name_is_utf8 = TRUE;
523
+ VALUE ret = rb_hash_new();
524
+
525
+ Data_Get_Struct(self, Display*, ptr);
526
+ disp = *ptr;
527
+
528
+ if (! (sup_window = (Window *)get_property(disp, DefaultRootWindow(disp),
529
+ XA_WINDOW, "_NET_SUPPORTING_WM_CHECK", NULL))) {
530
+ if (! (sup_window = (Window *)get_property(disp, DefaultRootWindow(disp),
531
+ XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK", NULL))) {
532
+ fputs("Cannot get window manager info properties.\n"
533
+ "(_NET_SUPPORTING_WM_CHECK or _WIN_SUPPORTING_WM_CHECK)\n", stderr);
534
+ return EXIT_FAILURE;
535
+ }
536
+ }
537
+
538
+ /* WM_NAME */
539
+ if (! (wm_name = get_property(disp, *sup_window,
540
+ XInternAtom(disp, "UTF8_STRING", False), "_NET_WM_NAME", NULL))) {
541
+ name_is_utf8 = FALSE;
542
+ if (! (wm_name = get_property(disp, *sup_window,
543
+ XA_STRING, "_NET_WM_NAME", NULL))) {
544
+ p_verbose("Cannot get name of the window manager (_NET_WM_NAME).\n");
545
+ }
546
+ }
547
+
548
+ /* WM_CLASS */
549
+ if (! (wm_class = get_property(disp, *sup_window,
550
+ XInternAtom(disp, "UTF8_STRING", False), "WM_CLASS", NULL))) {
551
+ name_is_utf8 = FALSE;
552
+ if (! (wm_class = get_property(disp, *sup_window,
553
+ XA_STRING, "WM_CLASS", NULL))) {
554
+ p_verbose("Cannot get class of the window manager (WM_CLASS).\n");
555
+ }
556
+ }
557
+
558
+ /* WM_PID */
559
+ if (! (wm_pid = (unsigned long *)get_property(disp, *sup_window,
560
+ XA_CARDINAL, "_NET_WM_PID", NULL))) {
561
+ p_verbose("Cannot get pid of the window manager (_NET_WM_PID).\n");
562
+ }
563
+
564
+ /* _NET_SHOWING_DESKTOP */
565
+ if (! (showing_desktop = (unsigned long *)get_property(disp, DefaultRootWindow(disp),
566
+ XA_CARDINAL, "_NET_SHOWING_DESKTOP", NULL))) {
567
+ p_verbose("Cannot get the _NET_SHOWING_DESKTOP property.\n");
568
+ }
569
+
570
+ rb_hash_aset(ret, key_name, (wm_name ? RB_UTF8_STRING_NEW2(wm_name) : Qnil));
571
+ rb_hash_aset(ret, key_class, (wm_class ? RB_UTF8_STRING_NEW2(wm_class) : Qnil));
572
+ rb_hash_aset(ret, key_pid, (wm_pid ? UINT2NUM(*wm_pid) : Qnil));
573
+ rb_hash_aset(ret, key_showing_desktop,
574
+ (showing_desktop ? RB_UTF8_STRING_NEW2(*showing_desktop == 1 ? "ON" : "OFF") : Qnil));
575
+
576
+ g_free(sup_window);
577
+ g_free(wm_name);
578
+ g_free(wm_class);
579
+ g_free(wm_pid);
580
+ g_free(showing_desktop);
581
+
582
+ return ret;
583
+ }
584
+
585
+ static VALUE rb_wmctrl_showing_desktop (VALUE self, VALUE state) {
586
+ Display **ptr, *disp;
587
+ Data_Get_Struct(self, Display*, ptr);
588
+ disp = *ptr;
589
+ client_msg(disp, DefaultRootWindow(disp), "_NET_SHOWING_DESKTOP", RTEST(state), 0, 0, 0, 0);
590
+ return Qtrue;
591
+ }
592
+
593
+ static VALUE rb_wmctrl_change_viewport (VALUE self, VALUE xnum, VALUE ynum) {
594
+ long x, y;
595
+ Display **ptr, *disp;
596
+ Data_Get_Struct(self, Display*, ptr);
597
+ disp = *ptr;
598
+ x = NUM2LONG(xnum);
599
+ y = NUM2LONG(ynum);
600
+ if (x < 0 || y < 0) {
601
+ rb_raise(rb_eArgError, "Arguments must be nonnegative integers.");
602
+ }
603
+ client_msg(disp, DefaultRootWindow(disp), "_NET_DESKTOP_VIEWPORT", x, y, 0, 0, 0);
604
+ return Qtrue;
605
+ }
606
+
607
+ static VALUE rb_wmctrl_change_geometry (VALUE self, VALUE xnum, VALUE ynum) {
608
+ long x, y;
609
+ Display **ptr, *disp;
610
+ Data_Get_Struct(self, Display*, ptr);
611
+ disp = *ptr;
612
+ x = NUM2LONG(xnum);
613
+ y = NUM2LONG(ynum);
614
+ if (x < 0 || y < 0) {
615
+ rb_raise(rb_eArgError, "Arguments must be nonnegative integers.");
616
+ }
617
+ client_msg(disp, DefaultRootWindow(disp), "_NET_DESKTOP_GEOMETRY", x, y, 0, 0, 0);
618
+ return Qtrue;
619
+ }
620
+
621
+ static VALUE rb_wmctrl_change_number_of_desktops (VALUE self, VALUE num) {
622
+ long n;
623
+ Display **ptr, *disp;
624
+ Data_Get_Struct(self, Display*, ptr);
625
+ disp = *ptr;
626
+ n = NUM2LONG(num);
627
+ if (n < 0) {
628
+ rb_raise(rb_eArgError, "An argument must be a nonnegative integer.");
629
+ }
630
+ client_msg(disp, DefaultRootWindow(disp), "_NET_NUMBER_OF_DESKTOPS", n, 0, 0, 0, 0);
631
+ return Qtrue;
632
+ }
633
+
634
+ static int activate_window (Display *disp, Window win, gboolean switch_desktop) {
635
+ unsigned long *desktop;
636
+
637
+ /* desktop ID */
638
+ if ((desktop = (unsigned long *)get_property(disp, win,
639
+ XA_CARDINAL, "_NET_WM_DESKTOP", NULL)) == NULL) {
640
+ if ((desktop = (unsigned long *)get_property(disp, win,
641
+ XA_CARDINAL, "_WIN_WORKSPACE", NULL)) == NULL) {
642
+ p_verbose("Cannot find desktop ID of the window.\n");
643
+ }
644
+ }
645
+
646
+ if (switch_desktop && desktop) {
647
+ if (client_msg(disp, DefaultRootWindow(disp),
648
+ "_NET_CURRENT_DESKTOP",
649
+ *desktop, 0, 0, 0, 0) != EXIT_SUCCESS) {
650
+ p_verbose("Cannot switch desktop.\n");
651
+ }
652
+ g_free(desktop);
653
+ }
654
+
655
+ client_msg(disp, win, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
656
+ XMapRaised(disp, win);
657
+
658
+ return True;
659
+ }
660
+
661
+ static int close_window (Display *disp, Window win) {
662
+ return client_msg(disp, win, "_NET_CLOSE_WINDOW", 0, 0, 0, 0, 0);
663
+ }
664
+
665
+ static gboolean wm_supports (Display *disp, const gchar *prop) {
666
+ Atom xa_prop = XInternAtom(disp, prop, False);
667
+ Atom *list;
668
+ unsigned long size;
669
+ unsigned int i;
670
+
671
+ if (! (list = (Atom *)get_property(disp, DefaultRootWindow(disp),
672
+ XA_ATOM, "_NET_SUPPORTED", &size))) {
673
+ p_verbose("Cannot get _NET_SUPPORTED property.\n");
674
+ return FALSE;
675
+ }
676
+
677
+ for (i = 0; i < size / sizeof(Atom); i++) {
678
+ if (list[i] == xa_prop) {
679
+ g_free(list);
680
+ return TRUE;
681
+ }
682
+ }
683
+
684
+ g_free(list);
685
+ return FALSE;
686
+ }
687
+
688
+ static int window_move_resize (Display *disp, Window win, signed long grav,
689
+ signed long x, signed long y, signed long w, signed long h) {
690
+ unsigned long grflags;
691
+ grflags = grav;
692
+ if (x != -1) grflags |= (1 << 8);
693
+ if (y != -1) grflags |= (1 << 9);
694
+ if (w != -1) grflags |= (1 << 10);
695
+ if (h != -1) grflags |= (1 << 11);
696
+
697
+ p_verbose("grflags: %lu\n", grflags);
698
+
699
+ if (wm_supports(disp, "_NET_MOVERESIZE_WINDOW")){
700
+ return client_msg(disp, win, "_NET_MOVERESIZE_WINDOW",
701
+ grflags, (unsigned long)x, (unsigned long)y, (unsigned long)w, (unsigned long)h);
702
+ }
703
+ else {
704
+ p_verbose("WM doesn't support _NET_MOVERESIZE_WINDOW. Gravity will be ignored.\n");
705
+ if ((w < 1 || h < 1) && (x >= 0 && y >= 0)) {
706
+ XMoveWindow(disp, win, x, y);
707
+ }
708
+ else if ((x < 0 || y < 0) && (w >= 1 && h >= -1)) {
709
+ XResizeWindow(disp, win, w, h);
710
+ }
711
+ else if (x >= 0 && y >= 0 && w >= 1 && h >= 1) {
712
+ XMoveResizeWindow(disp, win, x, y, w, h);
713
+ }
714
+ return EXIT_SUCCESS;
715
+ }
716
+ }
717
+
718
+ static int window_state (Display *disp, Window win,
719
+ const char *action_str, const char *prop1_str, const char *prop2_str) {
720
+ unsigned long action;
721
+ Atom prop1 = 0;
722
+ Atom prop2 = 0;
723
+ gchar *tmp_prop1, *tmp1;
724
+
725
+ /* action */
726
+ if (strcmp(action_str, "remove") == 0) {
727
+ action = _NET_WM_STATE_REMOVE;
728
+ }
729
+ else if (strcmp(action_str, "add") == 0) {
730
+ action = _NET_WM_STATE_ADD;
731
+ }
732
+ else if (strcmp(action_str, "toggle") == 0) {
733
+ action = _NET_WM_STATE_TOGGLE;
734
+ }
735
+ else {
736
+ rb_raise(rb_eArgError, "Invalid action. Use either remove, add or toggle.");
737
+ }
738
+
739
+ tmp_prop1 = g_strdup_printf("_NET_WM_STATE_%s", tmp1 = g_ascii_strup(prop1_str, -1));
740
+ p_verbose("State 1: %s\n", tmp_prop1);
741
+ prop1 = XInternAtom(disp, tmp_prop1, False);
742
+ g_free(tmp1);
743
+ g_free(tmp_prop1);
744
+
745
+ /* the second property */
746
+ if (prop2_str) {
747
+ gchar *tmp_prop2, *tmp2;
748
+ tmp_prop2 = g_strdup_printf("_NET_WM_STATE_%s", tmp2 = g_ascii_strup(prop2_str, -1));
749
+ p_verbose("State 2: %s\n", tmp_prop2);
750
+ prop2 = XInternAtom(disp, tmp_prop2, False);
751
+ g_free(tmp2);
752
+ g_free(tmp_prop2);
753
+ }
754
+ return client_msg(disp, win, "_NET_WM_STATE", action, (unsigned long)prop1, (unsigned long)prop2, 0, 0);
755
+ }
756
+
757
+ static int window_to_desktop (Display *disp, Window win, int desktop) {
758
+ unsigned long *cur_desktop = NULL;
759
+ Window root = DefaultRootWindow(disp);
760
+
761
+ if (desktop == -1) {
762
+ if (! (cur_desktop = (unsigned long *)get_property(disp, root,
763
+ XA_CARDINAL, "_NET_CURRENT_DESKTOP", NULL))) {
764
+ if (! (cur_desktop = (unsigned long *)get_property(disp, root,
765
+ XA_CARDINAL, "_WIN_WORKSPACE", NULL))) {
766
+ fputs("Cannot get current desktop properties. "
767
+ "(_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)"
768
+ "\n", stderr);
769
+ return EXIT_FAILURE;
770
+ }
771
+ }
772
+ desktop = *cur_desktop;
773
+ }
774
+ g_free(cur_desktop);
775
+
776
+ return client_msg(disp, win, "_NET_WM_DESKTOP", (unsigned long)desktop, 0, 0, 0, 0);
777
+ }
778
+
779
+ static void window_set_title (Display *disp, Window win, char *title, char mode) {
780
+ gchar *title_utf8;
781
+ gchar *title_local;
782
+
783
+ if (envir_utf8) {
784
+ title_utf8 = g_strdup(title);
785
+ title_local = NULL;
786
+ }
787
+ else {
788
+ if (! (title_utf8 = g_locale_to_utf8(title, -1, NULL, NULL, NULL))) {
789
+ title_utf8 = g_strdup(title);
790
+ }
791
+ title_local = g_strdup(title);
792
+ }
793
+
794
+ if (mode == 'T' || mode == 'N') {
795
+ /* set name */
796
+ if (title_local) {
797
+ XChangeProperty(disp, win, XA_WM_NAME, XA_STRING, 8, PropModeReplace,
798
+ (const unsigned char *)title_local, strlen(title_local));
799
+ }
800
+ else {
801
+ XDeleteProperty(disp, win, XA_WM_NAME);
802
+ }
803
+ XChangeProperty(disp, win, XInternAtom(disp, "_NET_WM_NAME", False),
804
+ XInternAtom(disp, "UTF8_STRING", False), 8, PropModeReplace,
805
+ (const unsigned char *)title_utf8, strlen(title_utf8));
806
+ }
807
+
808
+ if (mode == 'T' || mode == 'I') {
809
+ /* set icon name */
810
+ if (title_local) {
811
+ XChangeProperty(disp, win, XA_WM_ICON_NAME, XA_STRING, 8, PropModeReplace,
812
+ (const unsigned char *)title_local, strlen(title_local));
813
+ }
814
+ else {
815
+ XDeleteProperty(disp, win, XA_WM_ICON_NAME);
816
+ }
817
+ XChangeProperty(disp, win, XInternAtom(disp, "_NET_WM_ICON_NAME", False),
818
+ XInternAtom(disp, "UTF8_STRING", False), 8, PropModeReplace,
819
+ (const unsigned char *)title_utf8, strlen(title_utf8));
820
+ }
821
+
822
+ g_free(title_utf8);
823
+ g_free(title_local);
824
+ }
825
+
826
+ static int action_window (Display *disp, Window win, char mode, int argc, VALUE *argv) {
827
+ p_verbose("Using window: 0x%.8lx\n", win);
828
+ switch (mode) {
829
+ case 'a':
830
+ if (argc == 0) {
831
+ return activate_window(disp, win, TRUE);
832
+ }
833
+ case 'c':
834
+ if (argc == 0) {
835
+ return close_window(disp, win);
836
+ }
837
+ case 'e':
838
+ /* resize/move the window around the desktop => -r -e */
839
+ if (argc == 5) {
840
+ int grav, x, y, w, h;
841
+ grav = FIX2INT(argv[0]);
842
+ x = FIX2INT(argv[1]);
843
+ y = FIX2INT(argv[2]);
844
+ w = FIX2INT(argv[3]);
845
+ h = FIX2INT(argv[4]);
846
+ if (grav >= 0) {
847
+ return window_move_resize(disp, win, grav, x, y, w, h);
848
+ }
849
+ }
850
+ break;
851
+ case 'b':
852
+ /* change state of a window => -r -b */
853
+ if (argc == 2 || argc == 3) {
854
+ const char *action, *prop1, *prop2;
855
+ action = StringValuePtr(argv[0]);
856
+ prop1 = StringValuePtr(argv[1]);
857
+ if (argc == 3) {
858
+ prop2 = StringValuePtr(argv[2]);
859
+ } else {
860
+ prop2 = NULL;
861
+ }
862
+ return window_state(disp, win, action, prop1, prop2);
863
+ }
864
+ case 't':
865
+ /* move the window to the specified desktop => -r -t */
866
+ if (argc == 1) {
867
+ return window_to_desktop(disp, win, FIX2INT(argv[0]));
868
+ }
869
+ case 'R':
870
+ /* move the window to the current desktop and activate it => -r */
871
+ if (argc == 0) {
872
+ if (window_to_desktop(disp, win, -1)) {
873
+ usleep(100000); /* 100 ms - make sure the WM has enough
874
+ time to move the window, before we activate it */
875
+ return activate_window(disp, win, FALSE);
876
+ }
877
+ else {
878
+ return False;
879
+ }
880
+ }
881
+ case 'N': case 'I': case 'T':
882
+ if (argc == 1) {
883
+ window_set_title(disp, win, StringValuePtr(argv[0]), mode);
884
+ return True;
885
+ }
886
+ }
887
+ rb_raise(rb_eArgError, "Invalid argument of action_window.");
888
+ }
889
+
890
+ static Window Select_Window(Display *dpy) {
891
+ /*
892
+ * Routine to let user select a window using the mouse
893
+ * Taken from xfree86.
894
+ */
895
+
896
+ int status;
897
+ Cursor cursor;
898
+ XEvent event;
899
+ Window target_win = None, root = DefaultRootWindow(dpy);
900
+ int buttons = 0;
901
+ int dummyi;
902
+ unsigned int dummy;
903
+
904
+ /* Make the target cursor */
905
+ cursor = XCreateFontCursor(dpy, XC_crosshair);
906
+
907
+ /* Grab the pointer using target cursor, letting it room all over */
908
+ status = XGrabPointer(dpy, root, False,
909
+ ButtonPressMask|ButtonReleaseMask, GrabModeSync,
910
+ GrabModeAsync, root, cursor, CurrentTime);
911
+ if (status != GrabSuccess) {
912
+ fputs("ERROR: Cannot grab mouse.\n", stderr);
913
+ return 0;
914
+ }
915
+
916
+ /* Let the user select a window... */
917
+ while ((target_win == None) || (buttons != 0)) {
918
+ /* allow one more event */
919
+ XAllowEvents(dpy, SyncPointer, CurrentTime);
920
+ XWindowEvent(dpy, root, ButtonPressMask|ButtonReleaseMask, &event);
921
+ switch (event.type) {
922
+ case ButtonPress:
923
+ if (target_win == None) {
924
+ target_win = event.xbutton.subwindow; /* window selected */
925
+ if (target_win == None) target_win = root;
926
+ }
927
+ buttons++;
928
+ break;
929
+ case ButtonRelease:
930
+ if (buttons > 0) /* there may have been some down before we started */
931
+ buttons--;
932
+ break;
933
+ }
934
+ }
935
+
936
+ XUngrabPointer(dpy, CurrentTime); /* Done with pointer */
937
+
938
+ if (XGetGeometry (dpy, target_win, &root, &dummyi, &dummyi,
939
+ &dummy, &dummy, &dummy, &dummy) && target_win != root) {
940
+ target_win = XmuClientWindow (dpy, target_win);
941
+ }
942
+
943
+ return(target_win);
944
+ }
945
+
946
+ static Window get_active_window(Display *disp) {
947
+ char *prop;
948
+ unsigned long size;
949
+ Window ret = (Window)0;
950
+
951
+ prop = get_property(disp, DefaultRootWindow(disp), XA_WINDOW, "_NET_ACTIVE_WINDOW", &size);
952
+ if (prop) {
953
+ ret = *((Window*)prop);
954
+ g_free(prop);
955
+ }
956
+
957
+ return(ret);
958
+ }
959
+
960
+ static Window get_target_window (Display *disp, VALUE obj) {
961
+ Window wid = 0;
962
+ switch (TYPE(obj)) {
963
+ case T_FIXNUM:
964
+ wid = (Window) NUM2LONG(obj);
965
+ break;
966
+ /* case T_STRING: */
967
+ /* break; */
968
+ case T_SYMBOL:
969
+ {
970
+ ID sym_id = SYM2ID(obj);
971
+ if (sym_id == id_select) {
972
+ wid = Select_Window(disp);
973
+ } else if (sym_id == id_active) {
974
+ wid = get_active_window(disp);
975
+ }
976
+ }
977
+ break;
978
+ }
979
+ if (wid == 0) {
980
+ rb_raise(rb_eArgError, "Invalid target window object. It must be an integer or :active, :select");
981
+ }
982
+ return wid;
983
+ }
984
+
985
+ /* static int action_window_str (Display *disp) { */
986
+ /* Window activate = 0; */
987
+ /* Window *client_list; */
988
+ /* unsigned long client_list_size; */
989
+ /* int i; */
990
+
991
+ /* if ((client_list = get_client_list(disp, &client_list_size)) != NULL) { */
992
+ /* for (i = 0; i < client_list_size / sizeof(Window); i++) { */
993
+ /* gchar *match_utf8; */
994
+ /* if (options.show_class) { */
995
+ /* match_utf8 = get_window_class(disp, client_list[i]); /\* UTF8 *\/ */
996
+ /* } */
997
+ /* else { */
998
+ /* match_utf8 = get_window_title(disp, client_list[i]); /\* UTF8 *\/ */
999
+ /* } */
1000
+ /* if (match_utf8) { */
1001
+ /* gchar *match; */
1002
+ /* gchar *match_cf; */
1003
+ /* gchar *match_utf8_cf = NULL; */
1004
+ /* if (envir_utf8) { */
1005
+ /* match = g_strdup(options.param_window); */
1006
+ /* match_cf = g_utf8_casefold(options.param_window, -1); */
1007
+ /* } */
1008
+ /* else { */
1009
+ /* if (! (match = g_locale_to_utf8(options.param_window, -1, NULL, NULL, NULL))) { */
1010
+ /* match = g_strdup(options.param_window); */
1011
+ /* } */
1012
+ /* match_cf = g_utf8_casefold(match, -1); */
1013
+ /* } */
1014
+
1015
+ /* if (!match || !match_cf) { */
1016
+ /* continue; */
1017
+ /* } */
1018
+
1019
+ /* match_utf8_cf = g_utf8_casefold(match_utf8, -1); */
1020
+
1021
+ /* if ((options.full_window_title_match && strcmp(match_utf8, match) == 0) || */
1022
+ /* (!options.full_window_title_match && strstr(match_utf8_cf, match_cf))) { */
1023
+ /* activate = client_list[i]; */
1024
+ /* g_free(match); */
1025
+ /* g_free(match_cf); */
1026
+ /* g_free(match_utf8); */
1027
+ /* g_free(match_utf8_cf); */
1028
+ /* break; */
1029
+ /* } */
1030
+ /* g_free(match); */
1031
+ /* g_free(match_cf); */
1032
+ /* g_free(match_utf8); */
1033
+ /* g_free(match_utf8_cf); */
1034
+ /* } */
1035
+ /* g_free(client_list); */
1036
+ /* if (activate) { */
1037
+ /* break; */
1038
+ /* } */
1039
+ /* } */
1040
+ /* } */
1041
+ /* return activate; */
1042
+ /* } */
1043
+
1044
+ static VALUE rb_wmctrl_action_window(int argc, VALUE *argv, VALUE self) {
1045
+ Window wid;
1046
+ int mode;
1047
+ ID sym_id;
1048
+ Display **ptr, *disp;
1049
+ Data_Get_Struct(self, Display*, ptr);
1050
+ disp = *ptr;
1051
+ if (argc < 2) {
1052
+ rb_raise(rb_eArgError, "Need more than one argument.");
1053
+ }
1054
+ wid = get_target_window(disp, argv[0]);
1055
+ sym_id = SYM2ID(argv[1]);
1056
+ if (sym_id == id_activate) {
1057
+ mode = 'a';
1058
+ } else if (sym_id == id_close) {
1059
+ mode = 'c';
1060
+ } else if (sym_id == id_move_resize) {
1061
+ mode = 'e';
1062
+ } else if (sym_id == id_change_state) {
1063
+ mode = 'b';
1064
+ } else if (sym_id == id_move_to_desktop) {
1065
+ mode = 't';
1066
+ } else if (sym_id == id_move_to_current) {
1067
+ mode = 'R';
1068
+ } else if (sym_id == id_set_title_long) {
1069
+ mode = 'N';
1070
+ } else if (sym_id == id_set_title_short) {
1071
+ mode = 'I';
1072
+ } else if (sym_id == id_set_title_both) {
1073
+ mode = 'T';
1074
+ } else {
1075
+ rb_raise(rb_eStandardError, "Invalid argument of action_window.");
1076
+ }
1077
+ if (action_window(disp, wid, mode, argc - 2, (argv + 2))) {
1078
+ return Qtrue;
1079
+ } else {
1080
+ return Qfalse;
1081
+ }
1082
+ }
1083
+
1084
+ void Init_wmctrl()
1085
+ {
1086
+ rb_wmctrl_class = rb_define_class("WMCtrl", rb_cObject);
1087
+
1088
+ rb_define_alloc_func(rb_wmctrl_class, rb_wmctrl_alloc);
1089
+ rb_define_private_method(rb_wmctrl_class, "initialize", rb_wmctrl_initialize, -1);
1090
+
1091
+ rb_define_method(rb_wmctrl_class, "list_windows", rb_wmctrl_list_windows, 0);
1092
+ rb_define_method(rb_wmctrl_class, "list_desktops", rb_wmctrl_list_desktops, 0);
1093
+ rb_define_method(rb_wmctrl_class, "switch_desktop", rb_wmctrl_switch_desktop, 1);
1094
+ rb_define_method(rb_wmctrl_class, "info", rb_wmctrl_info, 0);
1095
+ rb_define_method(rb_wmctrl_class, "showing_desktop", rb_wmctrl_showing_desktop, 1);
1096
+ rb_define_method(rb_wmctrl_class, "change_viewport", rb_wmctrl_change_viewport, 0);
1097
+ rb_define_method(rb_wmctrl_class, "change_geometry", rb_wmctrl_change_geometry, 0);
1098
+ rb_define_method(rb_wmctrl_class, "change_number_of_desktops", rb_wmctrl_change_number_of_desktops, 1);
1099
+ rb_define_method(rb_wmctrl_class, "action_window", rb_wmctrl_action_window, -1);
1100
+
1101
+ key_id = ID2SYM(rb_intern("id"));
1102
+ key_title = ID2SYM(rb_intern("title"));
1103
+ key_pid = ID2SYM(rb_intern("pid"));
1104
+ key_geometry = ID2SYM(rb_intern("geometory"));
1105
+ key_class = ID2SYM(rb_intern("class"));
1106
+ key_client_machine = ID2SYM(rb_intern("client_machine"));
1107
+ key_desktop = ID2SYM(rb_intern("desktop"));
1108
+ key_viewport = ID2SYM(rb_intern("viewport"));
1109
+ key_workarea = ID2SYM(rb_intern("workarea"));
1110
+ key_current = ID2SYM(rb_intern("current"));
1111
+ key_showing_desktop = ID2SYM(rb_intern("showing_desktop"));
1112
+ key_name = ID2SYM(rb_intern("name"));
1113
+
1114
+ id_active = rb_intern("active");
1115
+ id_select = rb_intern("select");
1116
+ id_activate = rb_intern("activate");
1117
+ id_close = rb_intern("close");
1118
+ id_move_resize = rb_intern("move_resize");
1119
+ id_change_state = rb_intern("change_state");
1120
+ id_move_to_desktop = rb_intern("move_to_desktop");
1121
+ id_move_to_current = rb_intern("move_to_current");
1122
+ id_set_title_long = rb_intern("set_title_long");
1123
+ id_set_title_short = rb_intern("set_title_short");
1124
+ id_set_title_both = rb_intern("set_title_both");
1125
+ }