├── COPYING ├── ChangeLog ├── Makefile ├── README ├── doc └── lxctrl.html └── src ├── Makefile ├── lxctrl.c ├── xctrl.c └── xctrl.h /COPYING: -------------------------------------------------------------------------------- 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 | 294 | Copyright (C) 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 | , 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. 341 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | # NOTE: This is only a rough summary. For a more 3 | # detailed list of changes, see the git log. 4 | ########################################################## 5 | 6 | 2015-03-18: 7 | Moved source code repository from googlecode to github 8 | 9 | 2014-08-22: 10 | Reworked get_prop() to fix some problems on 64-bit platforms 11 | 12 | 2014-08-06: 13 | Added "title" and "state" events to listen() 14 | 15 | 2014-08-05: 16 | Added a function to "listen" for some window manager events 17 | 18 | 2014-08-01: 19 | Added a function to manipulate window decorations via _MOTIF_WM_HINTS 20 | 21 | 2014-08-01: 22 | Added a function to get the _NET_WM_WINDOW_TYPE of a window 23 | 24 | 2014-07-31: 25 | Added a function to retrieve a window's frame dimensions (borders) 26 | 27 | 2014-07-31: 28 | Changes to set_win_geom() function: accept a table or 4 numbers, 29 | use nil instead of negative, added optional "gravity" parameter. 30 | 31 | 2013-06-19: 32 | Lua 5.2 compatibility 33 | 34 | 2013-02-14: 35 | Various bug fixes 36 | 37 | 2011-10-15: 38 | Created git repo on googlecode 39 | 40 | 2010-10-26: 41 | Documentation fixes, renamed "lwmctrl.html" to "lxctrl.html" 42 | 43 | 2010-10-22: 44 | Initial public release 45 | 46 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKGNAME=xctrl 2 | 3 | default: 4 | @$(MAKE) --no-print-directory -C src 5 | 6 | %: 7 | @$(MAKE) --no-print-directory -C src $@ 8 | 9 | 10 | clean: 11 | $(RM) $(PKGNAME)-*.tar.gz 12 | $(MAKE) -C src clean 13 | 14 | 15 | dist: clean 16 | date +$(PKGNAME)-%Y-%m-%d > PKG_NAME 17 | rm -rf `cat PKG_NAME` 18 | mkdir -p `cat PKG_NAME` 19 | cp -rp * `cat PKG_NAME` 2>/dev/null || true 20 | rm -f `cat PKG_NAME`/PKG_NAME 21 | rm -rf `cat PKG_NAME`/`cat PKG_NAME` 22 | tar --owner=0 --group=0 -zcf `cat PKG_NAME`.tar.gz `cat PKG_NAME` 23 | rm -rf `cat PKG_NAME` 24 | rm -f PKG_NAME 25 | 26 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | The X11 control library is a shared library that allows you to query and 2 | manipulate various aspects of an X11 window manager and the windows it manages. 3 | 4 | It is based largely on Tomas Styblo's "wmctrl" command line tool, but has been 5 | transformed into a C library with some additional features and a Lua binding. 6 | 7 | It is intended to work with any EWMH / NetWM compatible window manager, but 8 | since support for these standards varies between different window managers and 9 | clients, some functionality might be missing in some environments. 10 | 11 | To build the Lua module, simply type "make" from the top-level directory, 12 | and "make install" to install install it in Lua's package.cpath. 13 | 14 | By default, the Makefile will use the first lua headers it can find. If your 15 | Lua headers are installed somewhere else, you can do something like: 16 | 17 | % make EXTRA_CFLAGS=-I/usr/include/lua5.1 18 | 19 | "make install" will use your "lua" executable to parse the package.cpath. 20 | If your Lua executable has another name, specify the name as LUA= when 21 | you install: 22 | 23 | % make install LUA=lua5.1 24 | 25 | You can also specify a non-standard install path with DESTDIR= 26 | 27 | % make install DESTDIR=~/my-lua-modules 28 | 29 | By default the Makefile builds a stripped library optimized for size, 30 | but you can disable optimizations and retain debugging information by 31 | using "make DEBUG=1" 32 | 33 | It is also possible to build a separate shared library for use in 34 | other applications by typing "make lib" but there is currently no 35 | "make install" target for that type of build. 36 | 37 | 38 | To link the C library statically into your own standalone application 39 | or library, define XCTRL_API as static and include the "xctrl.c" 40 | source file directly: 41 | 42 | #include 43 | #define XCTRL_API static 44 | #include "xctrl.c" 45 | 46 | 47 | 48 | The Lua binding should be fairly well documented, see the file: 49 | ./docs/lxctrl.html 50 | 51 | There is currently no documentation for the C library, but reading the 52 | sources and the Lua docs should give you some idea of what's there. 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /doc/lxctrl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Lua Window Manager Control Module 5 | 17 | 18 | 19 |

Window Manager Control Module


20 |



21 | The window manager control module (xctrl) is a Lua module which allows you to query and 22 | manipulate various aspects of an X11 window manager and the windows it manages. It is based 23 | largely on Tomas Styblo's wmctrl command line tool, but has been transformed 24 | into a C library with a Lua binding. It is intended to work with any EWMH / NetWM compatible 25 | window manager. Because support for these standards varies with different window managers 26 | and clients, some features might not work in all situations. 27 |

28 | The functions described below are intended to be used in object-oriented style, for example:

29 |
 30 |   require "xctrl"             -- load the module
 31 |   local xc=xctrl.new()        -- create a new xctrl object
 32 |   local win=xc:get_active_win()   -- find the currently active window
 33 |   xc:close_win(win)           -- close the window
 34 | 
35 |

36 | Note that only one instance of the xctrl object at a time is allowed to be in use. 37 | If for some reason you need to re-initialize a new object, you should set the old one 38 | to nil and call collectgarbage() twice before trying to create 39 | another new object. This will help insure that the first instance is properly cleaned up. 40 |

41 |

42 |
43 |

xctrl functions

44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
new (display [,as_utf8 [,charset]])-- Create a new xctrl object.
get_win_list ()-- Return a list of all top-level windows.
get_win_title (win)-- Get the title of a window.
set_win_title (win,title)-- Set the title of a window.
get_win_class ()-- Get the class name of a window.
activate_win (win [,switch])-- Raise and focus a window.
iconify_win(win)-- Change a window's state to iconic.
set_win_state (win,mode,p1 [,p2])-- Change a window's state properties.
set_win_geom (win,[x,y,w,h]|[t][,g])-- Move and/or resize a window
get_win_geom (win)-- Get a window's size and position
get_win_frame (win)-- Get the extents of a window's frame
get_win_type (win)-- Get the functional type of a window
set_win_decor (win, decor)-- Set the window manager decorations for a window
get_active_win ()-- Get the id of the currently active window.
pick_win ( [button] )-- Get the id of a window by clicking it with the mouse.
root_win ()-- Get the id of the display's root window.
set_desk_of_win (win,desk)-- Move a window onto another desktop.
get_desk_of_win (win)-- Get the index of a window's desktop.
get_pid_of_win (win)-- Get the process id of a window.
close_win (win)-- Try to close a window gracefully.
get_win_client (win)-- Get a window's client machine name.
get_num_desks ()-- Get the total number of desktops.
set_num_desks ()-- Set the total number of desktops.
get_curr_desk ()-- Get the index of the current active desktop.
set_curr_desk (desk)-- Switch to another desktop
get_display ()-- Return the the X11 display name.
get_wm_win ()-- Get the window manager's information window.
get_wm_name ()-- Get the window manager's name.
get_wm_class ()-- Get the window manager's class name.
get_wm_pid ()-- Get the window manager's process id.
get_desk_name (desk)-- Get the name of a desktop.
get_workarea (desk)-- Get the effective working dimensions of a desktop.
get_desk_geom (desk)-- Get the overall dimensions of a desktop.
set_desk_vport (desk,x,y)-- Set a desktop's viewport x-y coordinates.
set_desk_geom (desk,w,h)-- Set a desktop's dimensions.
set_showing_desk (showing)-- Set window manager's "showing desktop" mode.
get_showing_desk ()-- Get window manager's "showing desktop" mode.
set_selection(text[,mode[,utf8]])-- Push text into X selection, clipboard, or cut buffer.
get_selection ( [mode [,utf8] )-- Retreive text from X selection, clipboard, or cut buffer.
send_keys (win,keys)-- Send simulated keyboard actions to a window.
do_events( [count] )-- Flush the X server's event queue.
listen (event_handler)-- Monitor the window manager for events.
convert_locale (str,from,to)-- Convert string between locales.
133 |
134 |

new ([display [,as_utf8 [,charset]]])

135 |

136 | Returns a new xctrl object. The default display is used if none was specified. 137 | If the optional as_utf8 argument is true, 138 | xctrl will attempt to use UTF-8 encoding for all strings passed to and from X11. 139 | If the optional charset string argument is present, it will try to use that value 140 | for character set conversions, otherwise it will use the $CHARSET environment variable if 141 | present, or fall back to a default of ISO_8859-1 . 142 | 143 |

144 |

get_win_list ()

145 |

146 | Returns an indexed list of all top-level window id numbers on the current display. 147 |

148 |

get_win_title (win)

149 |

150 | Returns the title bar text of the specified window id. 151 |

152 |

set_win_title (win,title)

153 |

154 | Sets the title bar text of the specified window id. 155 |

156 |

get_win_class ()

157 |

158 | Returns the X11 class name (WM_CLASS property) of the specified window id. 159 |

160 |

activate_win (win [,switch])

161 |

162 | Raises the specified window and gives it the focus. If the window is not on the current desktop, 163 | the desktop containing the window will also be activated, unless the optional switch 164 | argument is false. 165 |

166 |

iconify_win (win)

167 |

168 | Changes the specified window's state to iconic or "minimized". 169 |

170 |

set_win_state (win,mode,p1 [,p2])

171 |

172 | Modify up to two of the window's properties simultaneously. 173 | The property change is achived by using the _NET_WM_STATE request.

174 | The mode string argument must be one of: 175 | "add", "remove" or "toggle"

176 | The supported property names (for p1 and p2) are:
  177 | "modal", 178 | "sticky", 179 | "maximized_vert", 180 | "maximized_horz", 181 | "shaded", 182 | "skip_taskbar", 183 | "skip_pager", 184 | "hidden", 185 | "fullscreen", 186 | "above" 187 | and 188 | "below" .

189 | 190 | 191 |

192 |

set_win_geom (win,[x,y,w,h]|[t][,g])

193 |

194 | Adjust the location and/or size of the window. 195 |

196 | The x, y, w, h arguments 197 | are usually integers, but any of them may be nil, in which case that value 198 | will be ignored (i.e. unchanged.) 199 |

200 | For the alternate form, t is a table in the same format as returned 201 | by get_win_geom().

202 | Thus the following two calls are equivalent:
203 |   set_win_geom(win, 32, 64, 640, 480)
204 |   set_win_geom(win, {x=32,y=64,w=640,h=480}) 205 |

206 | The final argument g is optional and specifies the "gravity" (direction) of 207 | a resize operation.
208 | If present, it must be one of the following strings:
  209 | "default", 210 | "northwest", 211 | "north", 212 | "northeast", 213 | "west", 214 | "center", 215 | "east", 216 | "southwest", 217 | "south", 218 | "southeast", 219 | "static" 220 |

221 |

get_win_geom (win)

222 |

223 | If successful, returns a table containing the x, y, 224 | w and h values of the specified window.

225 | On failure, returns nil plus an error message. 226 |

227 |

get_win_frame (win)

228 |

229 | If successful, returns a table containing the keys l, r, 230 | t, b which represent (respectively) the left, right, top, 231 | and bottom extents of the frame for the specified window.

232 | On failure, returns nil plus an error message.

233 |

234 |

get_win_type (win)

235 |

236 | Returns a string representing the functional type of the specified window.

237 | The returned string will be one of: 238 |
  "desktop" -- A sreen-sized window containing desktop icons, usually kept below other windows. 239 |
  "dock" -- A dock or panel, usually kept on top of all other windows. 240 |
  "toolbar" -- An undocked or "torn off" toolbar window. 241 |
  "menu" -- A "torn off" menu panel. 242 |
  "utility" -- A small persistent window, such as a palette or toolbox. 243 |
  "splash" -- A splash screen, temporarily displayed on application start-up. 244 |
  "dialog" -- A dialog box or "transient" window. 245 |
  "normal" -- A normal, top-level window. 246 |
  "unsupported" -- The window manager does not support this property. 247 |

248 | Note: Not all windows set this property, even if the window manager supports it. If a window's 249 | _NET_WM_WINDOW_TYPE property is not set, the type is assumed to be 250 | "dialog" if it has a WM_TRANSIENT_FOR property, or "normal" otherwise. 251 |

252 |

set_win_decor (win, decor)

253 |

Attempts to set the window manager decorations for the specified window.

254 | The decor argument is a table containing a list of strings, the following strings 255 | are valid elements for the table: 256 |
  "none" -- The window will have no decorations. 257 |
  "all" -- The window will have all decorations. 258 |
  "border" -- The window will have a border. 259 |
  "resize" -- The window will be resizable. 260 |
  "title" -- The window will have a title bar. 261 |
  "maximize" -- The title bar will have a maximize button. 262 |
  "close" -- The title bar will have a close button. 263 |

264 | Note: When the "none" or "all" string is used, 265 | it must be the only item in the list. 266 |

267 |

get_active_win ()

268 |

269 | Returns the window id number of the currently active window. 270 |

271 |

pick_win ( [button] )

272 |

273 | Displays a "crosshair" mouse cursor allowing the user to select a window by mouse click.

274 | By default any button click selects the window, but the optional button argument 275 | allows you to specify which mouse button (1,2,3) will trigger the selection event. 276 | For example, you might want to ignore a left-click to minimize some other windows, 277 | and instead use a right-click (the third button) to pick the window you want. 278 |

Caution: Don't specify button number three if you only have a two-button mouse! 279 |

280 | Returns the window id number of the clicked window. 281 |

282 |

root_win ()

283 |

284 | Returns the window id of the window manager's root window. 285 |

286 |

set_desk_of_win (win,desk)

287 |

288 | Moves the window to the desktop at the specified desk index.

289 | (Note that the desktop indexes in Lua begin at one, not zero.) 290 |

291 |

get_desk_of_win (win)

292 |

293 | Returns the index of the desktop that contains the specified window. 294 |

295 |

get_pid_of_win (win)

296 |

297 | Returns the process id (PID) of the specified window, provided the application exports 298 | the _NET_WM_PID property. 299 |

300 |

close_win (win)

301 | Attempts to gracefully close the specified window. 302 |

303 | 304 |

305 |

get_win_client (win)

306 |

307 | Returns the client machine's name, provided the application exports 308 | the WM_CLIENT_MACHINE property. 309 |

310 |

get_num_desks ()

311 |

312 | Returns the configured number of desktops for the window manager. 313 |

314 |

set_num_desks ()

315 |

316 | Changes the configured number of desktops for the window manager.

317 |

(Not all window managers allow this.) 318 |

319 |

get_curr_desk ()

320 |

321 | Returns the index number of the active desktop.

322 | (Note that the desktop indexes in Lua begin at one, not zero.) 323 |

324 |

set_curr_desk (desk)

325 |

326 | Switch to the desktop at index desk. 327 |

328 |

get_display ()

329 |

330 | Returns the name of the current display. 331 |

332 |

get_wm_win ()

333 |

334 | Returns the window id of the window manager's SUPPORTING_WM_CHECK window.

335 | This value is of little use, but unless it returns non-zero the 336 | get_wm_name, 337 | get_wm_class and 338 | get_wm_pid 339 | functions will also fail. 340 |

341 |

get_wm_name ()

342 |

343 | Returns the window manager's name property (if supported). 344 |

345 |

get_wm_class ()

346 |

347 | Returns the window manager's class property (if supported). 348 |

349 |

get_wm_pid ()

350 |

351 | Returns the window manager's process id (if supported). 352 |

353 |

get_desk_name (desk)

354 |

355 | Returns the name of the desktop at index desk. 356 |

357 |

get_workarea (desk)

358 |

359 | Returns a table containing the x, y, 360 | w and h values of the effective working area 361 | for the desktop at index desk. This may be smaller than the 362 | actual desktop dimensions since it should exclude "reserved" areas like struts and taskbars. 363 |

364 |

get_desk_geom (desk)

365 |

366 | Returns a table containing the x, y, 367 | w and h values of the overall dimensions 368 | for the desktop at index desk. 369 |

370 |

set_desk_vport (desk,x,y)

371 |

372 | Sets the top left coordinates of the desktop (if supported). 373 |

374 |

set_desk_geom (desk,w,h)

375 |

376 | Sets the width and height of the desktop (if supported). 377 |

378 |

set_showing_desk (showing)

379 |

380 | Sets the window manager's _NET_SHOWING_DESKTOP property (if supported). 381 |

382 |

get_showing_desk ()

383 |

384 | Returns the window manager's _NET_SHOWING_DESKTOP property (if supported). 385 |

386 | 387 |

set_selection (text [,mode [,utf8]] )

388 |

389 | Pushes the text into the X selection, clipboard, or cut buffer.

390 | The mode argument determines where the text is sent, and can be one of: 391 |
392 |   "p" : The primary selection (default).
393 |   "s" : The secondary selection.
394 |   "c" : The clipboard.
395 |   "b" : The cut buffer.
396 |

397 | If the optional utf8 argument is true, the text 398 | is considered to be in UTF-8 format. 399 |

400 | Note: For all modes except "b" this function is blocking, 401 | and will not return until another application requests the text or steals the selection. 402 |

403 | 404 | 405 |

get_selection ( [mode [,utf8]] )

406 |

407 | Retreives text from X selection, clipboard, or cut buffer.

408 | The mode argument determines which item is requested, 409 | as described in the set_selection() function.

410 | If the optional utf8 argument is true, the text 411 | is requested to be in UTF-8 format. 412 |

413 | 414 | 415 |

send_keys (win,keys)

416 |

Sends a series of keystrokes to the specified window, as if they were typed on the keyboard.

417 | Most printable characters are passed verbatim, but the following sequences are special:

418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 |
\nThe  Enter  key.
\tThe  Tab  key.
\27The  Esc  key.
\bThe  Backspace  key.
^The next key is passed with the  Ctrl  key pressed.
~The next key is passed with the  Alt  key pressed.
+The next key is passed with the  Shift  key pressed.
%^A literal  ^
%~A literal  ~
%+A literal  +
%%A literal  %
%FxxA function key, where xx is  01  thru  12
%.The  Delete  key.
%0The  Insert  key.
%1The  End  key.
%2The  Down  arrow key.
%3The  PageDown  key.
%4The  Left  arrow key.
%6The  Right  arrow key.
%7The  Home  key.
%8The  Up  arrow key.
%9The  PageUp  key.
442 |

Hint: Key sequences for the delete, insert and navigation keys correspond to the alternate 443 | functions of the numeric keypad on a standard U.S. keyboard.

444 |

445 | Since this function only sends keystrokes to the application's top-level window, complex 446 | applications with nested widgets and cascading menus might not exhibit consistent results. 447 |

448 |

do_events ( [count] )

449 |

450 | Causes the library to sleep for approximately one-tenth of a second, and then sends an XSync() 451 | message to the display, which flushes the event queue and waits until all requests have been 452 | received and processed by the X server. This process is performed count 453 | number of times (default: once). 454 |

455 | 456 | 457 |

listen ( handler )

458 |

459 | Continuously "listens" for window manager events, and runs the function handler 460 | each time an event occurs.

461 | The event handler function should be in the form of: 462 |

  function event_handler(ev,id)
463 |     -- handle the event here
464 |     return true
465 |   end
466 | 
467 |

468 | Note that the handler must return true to continue listening, 469 | or false to stop listening. 470 |

471 | The value of the string ev will be one of:
472 |   "w" -- A new window was created.
473 |   "x" -- The window was closed (its id is no longer valid!).
474 |   "a" -- The window was activated (gained focus).
475 |   "i" -- The window became inactive (lost focus).
476 |   "g" -- The window's exterior geometry changed (size, position, or decoration).
477 |   "t" -- The window's title text changed.
478 |   "s" -- The window's state changed (iconified, maximized, sticky, layer, etc.)
479 |   "d" -- Switched desktops: in this case id is the index of the activated desktop.
480 |

481 | Unless noted above, the value of the integer id is the ID of the window 482 | that triggered the event.

483 | Note: Rather than using expensive string comparisons on the value of ev, it may be 484 | more efficient to set up a table of functions as illustrated below: 485 |

486 |   local xc=xctrl.new()
487 | 
488 |   local event_callbacks = {
489 |     w = function(id) 
490 |           io.write("A new window ",id, " was created.\n")
491 |           return true 
492 |         end,
493 |     x = function(id)
494 |           io.write("The window ", id, " was closed.\n")
495 |           return true
496 |         end,
497 |     a = function(id)
498 |           io.write("The window ", id, " was activated.\n")
499 |           return true
500 |         end,
501 |     i = function(id)
502 |           io.write("The window ", id, " became inactive.\n")
503 |           return true
504 |         end,
505 |     g = function(id) 
506 |           io.write("The window ", id, " geometry changed.\n") 
507 |           return true
508 |         end,
509 |     t = function(id) 
510 |         io.write("The window ", id, " title changed.\n") 
511 |         return true
512 |       end,
513 |     s = function(id) 
514 |         io.write("The window ", id, " state changed.\n") 
515 |         return true
516 |       end,
517 |     d = function(id) 
518 |           io.write("Switched to desktop number ", id, ".\n")
519 |           return true
520 |         end
521 |   }
522 | 
523 |   function my_event_handler(ev,id)
524 |     local callback=event_callbacks[ev]
525 |     if callback ~= nil then
526 |       return callback(id)
527 |     else
528 |       return true
529 |     end
530 |   end
531 | 
532 |   xc:listen(my_event_handler)
533 | 
534 | 535 |

536 | 537 | 538 | 539 | 540 | 541 |

convert_locale (str,from,to)

542 |

543 | Converts the string str from its original encoding from 544 | into the new encoding to and returns the new string. 545 |

546 |
547 |






548 | 549 | 550 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | DLL_EXT=.so 2 | LIBNAME=libxctrl${DLL_EXT} 3 | MODNAME=xctrl${DLL_EXT} 4 | 5 | LUA=lua 6 | 7 | INSTALL=install 8 | DESTDIR=`${LUA} -e 'p=package.cpath:gsub(".-(/[^;]+/).*","%1");print(p)'` 9 | 10 | WARN_FLAGS=-Wall -pedantic -Wshadow -Wunused -Wbad-function-cast -Wmissing-prototypes 11 | VERSION=1.09 12 | 13 | CFLAGS= ${EXTRA_CFLAGS} -Wall -DVERSION=\"$(VERSION)\" 14 | LDFLAGS=${EXTRA_LDFLAGS} -lX11 -lXmu 15 | 16 | ifeq ($(DEBUG), 1) 17 | LDFLAGS += -ggdb3 18 | CFLAGS += -O0 -ggdb3 19 | else 20 | ifeq ($(SYMS), 1) 21 | CFLAGS += -Os -fno-inline 22 | LDFLAGS += 23 | else 24 | CFLAGS += -Os 25 | LDFLAGS += -s 26 | endif 27 | endif 28 | CFLAGS += ${WARN_FLAGS} 29 | 30 | 31 | lua: $(MODNAME)_clean $(MODNAME) 32 | 33 | lib: $(LIBNAME)_clean $(LIBNAME) 34 | 35 | $(LIBNAME): xctrl.c 36 | $(CC) $(CFLAGS) $(LDFLAGS) -shared $^ -o $@ 37 | 38 | 39 | $(MODNAME): lxctrl.c 40 | $(CC) $(CFLAGS) $(LDFLAGS) -shared $^ -o $@ 41 | 42 | 43 | %.o: %.c 44 | $(CC) $(CFLAGS) -c $< 45 | 46 | 47 | $(LIBNAME)_clean: 48 | rm -f *.o $(LIBNAME) 49 | 50 | $(MODNAME)_clean: 51 | rm -f *.o $(MODNAME) 52 | 53 | install: $(MODNAME) 54 | $(INSTALL) -d -m 755 ${DESTDIR} 55 | $(INSTALL) -m 755 $^ ${DESTDIR} 56 | @echo 57 | @echo Installed \"$^\" in ${DESTDIR} "(OK)" 58 | @echo 59 | 60 | 61 | clean: 62 | $(RM) *.o $(LIBNAME) $(MODNAME) 63 | 64 | -------------------------------------------------------------------------------- /src/lxctrl.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Lua bindings to the wmctrl window manager control library. 4 | 5 | (C) 2010 by Jeffrey Pohlmeyer 6 | 7 | 8 | This program is free software, released under the GNU General Public 9 | License. You may redistribute and/or modify this program under the terms 10 | of that license as published by the Free Software Foundation; either 11 | version 2 of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | To get a copy of the GNU General Puplic License, write to the 19 | Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define XCTRL_API static 30 | #include "xctrl.c" 31 | #undef XCTRL_API 32 | 33 | 34 | 35 | #define XCTRL_META_NAME "xctrl" 36 | 37 | #define SetTableValue(name,value,pusher) \ 38 | lua_pushstring(L, name); \ 39 | pusher(L, value); \ 40 | lua_rawset(L,-3); 41 | 42 | #define SetTableStr(name,value) SetTableValue(name,value,lua_pushstring) 43 | #define SetTableBool(name,value) SetTableValue(name,value,lua_pushboolean) 44 | #define SetTableNum(name,value) SetTableValue(name,(lua_Number)value,lua_pushnumber) 45 | 46 | #define ERR_BUF_SIZE 128 47 | 48 | static char lwmc_error_buffer[ERR_BUF_SIZE]; 49 | 50 | static int lwmc_handle_error(Display*dpy, XErrorEvent*ev) 51 | { 52 | memset(lwmc_error_buffer, '\0', ERR_BUF_SIZE); 53 | if ( !ev ) { 54 | strncpy(lwmc_error_buffer, "NULL event\n", ERR_BUF_SIZE-1); 55 | } else if ( !dpy ) { 56 | strncpy(lwmc_error_buffer, "NULL display\n", ERR_BUF_SIZE-1); 57 | } else { 58 | XGetErrorText(dpy, ev->error_code, lwmc_error_buffer, ERR_BUF_SIZE-1); 59 | } 60 | return -1; 61 | } 62 | 63 | 64 | 65 | typedef struct _XCtrl { 66 | Display* dpy; 67 | char *dpyname; 68 | char* charset; 69 | XErrorHandler old_err_handler; 70 | } XCtrl; 71 | 72 | static XCtrl*wm=NULL; 73 | 74 | 75 | static XCtrl*lwmc_check_obj(lua_State*L) { 76 | return (XCtrl*)luaL_checkudata(L,1,XCTRL_META_NAME); 77 | } 78 | 79 | 80 | 81 | static int lwmc_failure(lua_State*L, const char*msg) 82 | { 83 | lua_pushnil(L); 84 | lua_pushstring(L,msg); 85 | return 2; 86 | } 87 | 88 | 89 | 90 | static int lwmc_new(lua_State*L) 91 | { 92 | if (wm==NULL) { 93 | const char*req_dpyname=luaL_optstring(L,1,NULL); 94 | char*act_dpyname=XDisplayName(req_dpyname); 95 | Display*dpy=XOpenDisplay(req_dpyname); 96 | if (!dpy) { 97 | return lwmc_failure(L, "Can't open display."); 98 | } 99 | wm=(XCtrl*)lua_newuserdata(L,sizeof(XCtrl)); 100 | memset(wm,0,sizeof(XCtrl)); 101 | if (act_dpyname) { wm->dpyname=strdup(act_dpyname); } 102 | wm->dpy=dpy; 103 | luaL_getmetatable(L, XCTRL_META_NAME); 104 | lua_setmetatable(L, -2); 105 | wm->old_err_handler=XSetErrorHandler(lwmc_handle_error); 106 | if (lua_gettop(L)>1) { 107 | int force_utf8=lua_toboolean(L,2); 108 | const char*charset=luaL_optstring(L,3,NULL); 109 | if (charset) { wm->charset=strdup(charset); } 110 | init_charset(force_utf8,wm->charset); 111 | } 112 | } else { 113 | luaL_error(L,"Only one instance of "XCTRL_META_NAME" is allowed."); 114 | return 0; 115 | } 116 | return 1; 117 | } 118 | 119 | 120 | 121 | static int lwmc_gc(lua_State*L) 122 | { 123 | XCtrl*ud=lwmc_check_obj(L); 124 | XSetErrorHandler(wm->old_err_handler); 125 | XCloseDisplay(ud->dpy); 126 | if (wm->dpyname) { free(ud->dpyname); } 127 | if (wm->charset) { free(ud->charset); } 128 | wm=NULL; 129 | return 1; 130 | } 131 | 132 | 133 | 134 | static int lwmc_get_wm_win(lua_State*L) 135 | { 136 | XCtrl*ud=lwmc_check_obj(L); 137 | lua_pushnumber(L,supporting_wm_check(ud->dpy)); 138 | return 1; 139 | } 140 | 141 | 142 | 143 | static int lwmc_get_wm_name(lua_State*L) 144 | { 145 | XCtrl*ud=lwmc_check_obj(L); 146 | char*tmp=get_wm_name(ud->dpy); 147 | lua_pushstring(L,tmp); 148 | if (tmp) { free(tmp); } 149 | return 1; 150 | } 151 | 152 | 153 | 154 | static int lwmc_get_wm_class(lua_State*L) 155 | { 156 | XCtrl*ud=lwmc_check_obj(L); 157 | char*tmp=get_wm_class(ud->dpy); 158 | lua_pushstring(L,tmp); 159 | if (tmp) { free(tmp); } 160 | return 1; 161 | } 162 | 163 | 164 | 165 | static int lwmc_get_wm_pid(lua_State*L) 166 | { 167 | XCtrl*ud=lwmc_check_obj(L); 168 | lua_pushnumber(L,get_wm_pid(ud->dpy)); 169 | return 1; 170 | 171 | } 172 | 173 | 174 | 175 | static int lwmc_get_showing_desktop(lua_State*L) 176 | { 177 | XCtrl*ud=lwmc_check_obj(L); 178 | lua_pushnumber(L,get_showing_desktop(ud->dpy)); 179 | return 1; 180 | } 181 | 182 | 183 | 184 | static int lwmc_get_display(lua_State*L) 185 | { 186 | XCtrl*ud=lwmc_check_obj(L); 187 | lua_pushstring(L,ud->dpyname); 188 | return 1; 189 | } 190 | 191 | 192 | 193 | static Window check_window(lua_State*L, XCtrl*ud, int argnum) 194 | { 195 | Window win; 196 | memset(lwmc_error_buffer, '\0', ERR_BUF_SIZE); 197 | luaL_argcheck(L,lua_isnumber(L,argnum), argnum, "expected window id"); 198 | win=lua_tonumber(L,argnum); 199 | return win; 200 | } 201 | 202 | 203 | 204 | static Bool lwmc_success(lua_State*L, XCtrl*ud) 205 | { 206 | XSync(ud->dpy,False); 207 | if (lwmc_error_buffer[0]!=0) { 208 | lua_pushnil(L); \ 209 | lua_pushstring(L,lwmc_error_buffer); 210 | return False; 211 | } 212 | return True; 213 | } 214 | 215 | 216 | 217 | static int lwmc_get_win_class(lua_State*L) 218 | { 219 | XCtrl*ud=lwmc_check_obj(L); 220 | Window win=check_window(L,ud,2); 221 | char*s=get_window_class(ud->dpy, win); 222 | if (lwmc_success(L,ud)) { 223 | lua_pushstring(L,s?s:""); 224 | if (s) { free(s); } 225 | return 1; 226 | } else { 227 | if (s) { free(s); } 228 | return 2; 229 | } 230 | } 231 | 232 | 233 | 234 | static int lwmc_get_win_title(lua_State*L) 235 | { 236 | XCtrl*ud=lwmc_check_obj(L); 237 | Window win=check_window(L,ud,2); 238 | char*s=get_window_title(ud->dpy, win); 239 | if (lwmc_success(L,ud)) { 240 | lua_pushstring(L,s?s:""); 241 | if (s) { free(s); } 242 | return 1; 243 | } else { 244 | if (s) { free(s); } 245 | return 2; 246 | } 247 | } 248 | 249 | 250 | 251 | static int lwmc_set_win_title(lua_State*L) 252 | { 253 | XCtrl*ud=lwmc_check_obj(L); 254 | Window win=check_window(L,ud,2); 255 | const char*name=luaL_checkstring(L,3); 256 | const char*mode=luaL_optstring(L,4,"T"); 257 | luaL_argcheck(L, (strlen(mode)==1) && (strchr("NTI", mode[0])), 4, "mode must be 'T' 'I' or 'N'"); 258 | set_window_title(ud->dpy, win, name, mode[0]); 259 | if (lwmc_success(L,ud)) { 260 | lua_pushboolean(L,True); 261 | return 1; 262 | } else { return 2; } 263 | } 264 | 265 | 266 | 267 | static int lwmc_set_win_geom(lua_State*L) 268 | { 269 | static const char*gravities[] = { 270 | "default", 271 | "northwest", 272 | "north", 273 | "northeast", 274 | "west", 275 | "center", 276 | "east", 277 | "southwest", 278 | "south", 279 | "southeast", 280 | "static", 281 | NULL 282 | }; 283 | XCtrl*ud=lwmc_check_obj(L); 284 | Window win=check_window(L,ud,2); 285 | long x=0,y=0,w=1,h=1,g=0; 286 | long flags=0; 287 | int narg=lua_gettop(L); 288 | if (lua_istable(L,3)) { 289 | if (narg>=4) { g=luaL_checkoption(L,4,NULL,gravities); } 290 | lua_pushnil(L); /* make room for first key */ 291 | while (lua_next(L, 3) != 0) { /* walk the table */ 292 | if (lua_type(L, -2)==LUA_TSTRING) { /* 'key' is at index -2 */ 293 | const char*key=lua_tostring(L,-2); 294 | if ( key && key[0] && (!key[1]) && (lua_type(L, -1)==LUA_TNUMBER)) { 295 | long value=lua_tonumber(L,-1); /* 'value' is at index -1 */ 296 | switch (key[0]) { 297 | case 'x': { 298 | x=value; 299 | flags|=XCTRL_GEOM_USE_X; 300 | break; 301 | } 302 | case 'y': { 303 | y=value; 304 | flags|=XCTRL_GEOM_USE_Y; 305 | break; 306 | } 307 | case 'w': { 308 | w=value; 309 | flags|=XCTRL_GEOM_USE_W; 310 | break; 311 | } 312 | case 'h': { 313 | h=value; 314 | flags|=XCTRL_GEOM_USE_H; 315 | break; 316 | } 317 | } 318 | } 319 | } 320 | lua_pop(L, 1); 321 | } 322 | } else { 323 | if (!lua_isnil(L,3)) { 324 | x=luaL_checknumber(L,3); 325 | flags|=XCTRL_GEOM_USE_X; 326 | } 327 | if (narg>=4&&!lua_isnil(L,4)) { 328 | y=luaL_checknumber(L,4); 329 | flags|=XCTRL_GEOM_USE_Y; 330 | } 331 | if (narg>=5&&!lua_isnil(L,5)) { 332 | w=luaL_checknumber(L,5); 333 | flags|=XCTRL_GEOM_USE_W; 334 | } 335 | if (narg>=6&&!lua_isnil(L,6)) { 336 | h=luaL_checknumber(L,6); 337 | flags|=XCTRL_GEOM_USE_H; 338 | } 339 | if (narg>=7) { g=luaL_checkoption(L,7,NULL,gravities); } 340 | } 341 | if (set_window_geom(ud->dpy,win,g,flags,x,y,w,h)) { 342 | return lwmc_failure(L,"move/resize failed"); 343 | } else if (lwmc_success(L,ud)) { 344 | lua_pushboolean(L,True); 345 | return 1; 346 | } else { 347 | return 2; 348 | } 349 | } 350 | 351 | 352 | 353 | static int lwmc_close_win(lua_State*L) 354 | { 355 | XCtrl*ud=lwmc_check_obj(L); 356 | Window win=check_window(L,ud,2); 357 | if (close_window(ud->dpy, win)) { 358 | return lwmc_failure(L,"failed to close window"); 359 | } else if (lwmc_success(L,ud)) { 360 | lua_pushboolean(L,True); 361 | return 1; 362 | } else { 363 | return 2; 364 | } 365 | } 366 | 367 | 368 | 369 | static int lwmc_activate_win(lua_State*L) 370 | { 371 | XCtrl*ud=lwmc_check_obj(L); 372 | Window win=check_window(L,ud,2); 373 | int switch_desktop=True; 374 | if (lua_gettop(L)>2) { 375 | switch_desktop=lua_toboolean(L,3); 376 | } 377 | activate_window(ud->dpy, win, switch_desktop); 378 | if (lwmc_success(L,ud)) { 379 | lua_pushboolean(L,True); 380 | return 1; 381 | } else { 382 | return 2; 383 | } 384 | } 385 | 386 | 387 | 388 | static int lwmc_iconify_win(lua_State*L) 389 | { 390 | XCtrl*ud=lwmc_check_obj(L); 391 | Window win=check_window(L,ud,2); 392 | Bool rv=iconify_window(ud->dpy, win); 393 | if (lwmc_success(L,ud)) { 394 | if (rv) { 395 | lua_pushboolean(L,True); 396 | return 1; 397 | } else { 398 | return lwmc_failure(L,"failed to iconify window"); 399 | } 400 | } else { 401 | return 2; 402 | } 403 | } 404 | 405 | 406 | 407 | static int lwmc_set_desk_of_win(lua_State*L) 408 | { 409 | XCtrl*ud=lwmc_check_obj(L); 410 | Window win=check_window(L,ud,2); 411 | int desk=luaL_checknumber(L,3); 412 | int ok=send_window_to_desktop(ud->dpy,win,desk-1); 413 | if (lwmc_success(L,ud)) { 414 | if (ok) { 415 | lua_pushboolean(L,True); 416 | return 1; 417 | } else { 418 | return lwmc_failure(L,"sendto failed"); 419 | } 420 | } else { return 2; } 421 | } 422 | 423 | 424 | 425 | static int lwmc_get_desk_of_win(lua_State*L) 426 | { 427 | XCtrl*ud=lwmc_check_obj(L); 428 | Window win=check_window(L,ud,2); 429 | long desk=get_desktop_of_window(ud->dpy,win); 430 | if (lwmc_success(L,ud)) { 431 | if ( desk > -2 ) { 432 | lua_pushnumber(L,desk==-1?-1:desk+1); 433 | return 1; 434 | } else { 435 | return lwmc_failure(L,"can't find desktop"); 436 | } 437 | } else { return 2; } 438 | } 439 | 440 | 441 | 442 | static int lwmc_get_pid_of_win(lua_State*L) 443 | { 444 | XCtrl*ud=lwmc_check_obj(L); 445 | Window win=check_window(L,ud,2); 446 | long pid=get_win_pid(ud->dpy,win); 447 | if (lwmc_success(L,ud)) { 448 | if ( pid > 0 ) { 449 | lua_pushnumber(L,pid); 450 | return 1; 451 | } else { 452 | return lwmc_failure(L,"unsupported feature"); 453 | } 454 | } else { return 2; } 455 | } 456 | 457 | 458 | 459 | static int lwmc_get_win_client(lua_State*L) 460 | { 461 | XCtrl*ud=lwmc_check_obj(L); 462 | Window win=check_window(L,ud,2); 463 | char*cm=get_client_machine(ud->dpy,win); 464 | if (lwmc_success(L,ud)) { 465 | if ( cm ) { 466 | lua_pushstring(L,cm); 467 | free(cm); 468 | return 1; 469 | } else { 470 | return lwmc_failure(L,"unknown client"); 471 | } 472 | } else { 473 | if (cm) { free(cm); } 474 | return 2; 475 | } 476 | } 477 | 478 | 479 | 480 | static int lwmc_set_win_state(lua_State*L) 481 | { 482 | XCtrl*ud=lwmc_check_obj(L); 483 | Window win=check_window(L,ud,2); 484 | ulong action; 485 | const char *p1; 486 | const char *p2; 487 | const char* actions[]={"add", "remove", "toggle", NULL}; 488 | switch (luaL_checkoption(L,3,NULL,actions)) { 489 | case 0: action= _NET_WM_STATE_ADD; break; 490 | case 1: action= _NET_WM_STATE_REMOVE; break; 491 | default: action= _NET_WM_STATE_TOGGLE; break; 492 | } 493 | p1=luaL_checkstring(L,4); 494 | luaL_argcheck(L,p1&&*p1, 4, "property can't be empty"); 495 | p2=luaL_optstring(L,5,NULL); 496 | luaL_argcheck(L,(!p2)||*p2, 5, "property can't be empty"); 497 | set_window_state(ud->dpy,win,action, p1, p2); 498 | if (lwmc_success(L,ud)) { 499 | lua_pushboolean(L,True); 500 | return 1; 501 | } else { 502 | return 2; 503 | } 504 | } 505 | 506 | 507 | 508 | static int lwmc_pick_win(lua_State*L) 509 | { 510 | XCtrl*ud=lwmc_check_obj(L); 511 | int btn=-1; 512 | if (lua_gettop(L)>1) { 513 | btn=luaL_checknumber(L,2); 514 | luaL_argcheck(L,(btn>=1)&&(btn<=3),2,"Button must be between 1 and 3"); 515 | } 516 | 517 | lua_pushnumber(L,select_window(ud->dpy, btn)); 518 | return 1; 519 | } 520 | 521 | 522 | 523 | static int lwmc_get_active_win(lua_State*L) 524 | { 525 | XCtrl*ud=lwmc_check_obj(L); 526 | lua_pushnumber(L,get_active_window(ud->dpy)); 527 | return 1; 528 | } 529 | 530 | 531 | 532 | static int lwmc_root_win(lua_State*L) 533 | { 534 | XCtrl*ud=lwmc_check_obj(L); 535 | lua_pushnumber(L,DefaultRootWindow(ud->dpy)); 536 | return 1; 537 | } 538 | 539 | 540 | 541 | static int lwmc_get_win_geom(lua_State*L) { 542 | XCtrl*ud=lwmc_check_obj(L); 543 | Window win=check_window(L,ud,2); 544 | Geometry inf={0,0,0,0}; 545 | get_window_geom(ud->dpy, win, &inf); 546 | if (lwmc_success(L,ud)) { 547 | lua_newtable(L); 548 | SetTableNum("x", inf.x); 549 | SetTableNum("y", inf.y); 550 | SetTableNum("w", inf.w); 551 | SetTableNum("h", inf.h); 552 | return 1; 553 | } else { 554 | return 2; 555 | } 556 | } 557 | 558 | 559 | 560 | static int lwmc_get_win_frame(lua_State*L) 561 | { 562 | XCtrl*ud=lwmc_check_obj(L); 563 | Window win=check_window(L,ud,2); 564 | long left=0, right=0, top=0, bottom=0; 565 | if (!wm_supports(ud->dpy,"_NET_FRAME_EXTENTS")) { 566 | return lwmc_failure(L,"Unsupported window manager feature"); 567 | } 568 | if (get_window_frame(ud->dpy, win, &left, &right, &top, &bottom)) { 569 | if (lwmc_success(L,ud)) { 570 | lua_newtable(L); 571 | SetTableNum("l", left); 572 | SetTableNum("r", right); 573 | SetTableNum("t", top); 574 | SetTableNum("b", bottom); 575 | return 1; 576 | } else { 577 | return 2; 578 | } 579 | } else { 580 | return lwmc_failure(L,"Could not determine frame extents"); 581 | } 582 | } 583 | 584 | 585 | 586 | static int lwmc_get_win_type(lua_State*L) 587 | { 588 | XCtrl*ud=lwmc_check_obj(L); 589 | Window win=check_window(L,ud,2); 590 | char*type=get_window_type(ud->dpy, win); 591 | lua_pushstring(L,type); 592 | free(type); 593 | return 1; 594 | } 595 | 596 | 597 | static int lookup_mwm_hint(const char*a) 598 | { 599 | static const char* hints[] = { 600 | "none", /* 0 */ 601 | "all", /* 1 */ 602 | "border", /* 2 */ 603 | "resize", /* 3 */ 604 | "title", /* 4 */ 605 | "maximize", /* 5 */ 606 | "close", /* 6 */ 607 | NULL 608 | }; 609 | int i; 610 | for (i=0; hints[i]; i++) { 611 | if (strcmp(a,hints[i])==0) { return i; } 612 | } 613 | return -1; 614 | } 615 | 616 | 617 | #if LUA_VERSION_NUM>501 618 | #define TableLength lua_rawlen 619 | #else 620 | #define TableLength lua_objlen 621 | #endif 622 | 623 | static int lwmc_set_win_decor(lua_State*L) 624 | { 625 | XCtrl*ud=lwmc_check_obj(L); 626 | Window win=check_window(L,ud,2); 627 | int i,n; 628 | long flags=XCTRL_MWM_HINTS_FUNCTIONS|XCTRL_MWM_HINTS_DECORATIONS; 629 | long funcs=0; 630 | long decors=0; 631 | long imode=0; 632 | luaL_argcheck(L, lua_istable(L,3), 3, "expected table"); 633 | n=TableLength(L, 3); 634 | luaL_argcheck(L,n>0,3,"table must not be empty"); 635 | for (i=1; i<=n; i++) { 636 | lua_rawgeti(L, 3, i); 637 | if (lua_isstring(L,-1)) { 638 | switch (lookup_mwm_hint(lua_tostring(L, -1))) { 639 | case -1: { /* invalid */ 640 | return luaL_argerror(L,3,"unknown hint string in table"); 641 | } 642 | case 0: { /* none */ 643 | if (n>1) { return luaL_argerror(L,3,"hint string \"none\" must be used alone"); } 644 | decors=XCTRL_MWM_DECOR_NONE; 645 | funcs=XCTRL_MWM_FUNC_NONE; 646 | break; 647 | } 648 | case 1: { /* all */ 649 | if (n>1) { return luaL_argerror(L,3,"hint string \"all\" must be used alone"); } 650 | decors=XCTRL_MWM_DECOR_ALL; 651 | funcs=XCTRL_MWM_FUNC_ALL; 652 | break; 653 | } 654 | case 2: { /* border */ 655 | decors|=XCTRL_MWM_DECOR_BORDER; 656 | break; 657 | } 658 | case 3: { /* resize */ 659 | decors|=XCTRL_MWM_DECOR_BORDER|XCTRL_MWM_DECOR_RESIZEH; 660 | funcs|=XCTRL_MWM_FUNC_RESIZE; 661 | break; 662 | } 663 | case 4: { /* title */ 664 | decors|=XCTRL_MWM_DECOR_TITLE|XCTRL_MWM_DECOR_MENU; 665 | funcs|=XCTRL_MWM_FUNC_MOVE; 666 | } 667 | case 5: { /* maximize */ 668 | decors|=XCTRL_MWM_DECOR_MINIMIZE|XCTRL_MWM_DECOR_MAXIMIZE; 669 | funcs|=XCTRL_MWM_FUNC_MINIMIZE|XCTRL_MWM_FUNC_MAXIMIZE; 670 | } 671 | case 6: { /* close */ 672 | funcs|=XCTRL_MWM_FUNC_CLOSE; 673 | } 674 | } 675 | } else { 676 | return luaL_argerror(L,3,"table must be a list of strings"); 677 | } 678 | lua_pop(L,1); 679 | } 680 | set_window_mwm_hints(ud->dpy, win, flags, funcs, decors, imode); 681 | lua_pushboolean(L,1); 682 | return 1; 683 | } 684 | 685 | 686 | 687 | static int lwmc_get_workarea(lua_State*L) { 688 | XCtrl*ud=lwmc_check_obj(L); 689 | int desknum=luaL_checknumber(L,2); 690 | Geometry inf={0,0,0,0}; 691 | if (get_workarea_geom(ud->dpy, &inf, desknum-1)) { 692 | lua_newtable(L); 693 | SetTableNum("x", inf.x); 694 | SetTableNum("y", inf.y); 695 | SetTableNum("w", inf.w); 696 | SetTableNum("h", inf.h); 697 | } else { 698 | lua_pushnil(L); 699 | } 700 | return 1; 701 | } 702 | 703 | 704 | 705 | static int lwmc_get_desk_geom(lua_State*L) { 706 | XCtrl*ud=lwmc_check_obj(L); 707 | int desknum=luaL_checknumber(L,2); 708 | Geometry inf={0,0,0,0}; 709 | if (get_desktop_geom(ud->dpy, desknum-1, &inf)) { 710 | lua_newtable(L); 711 | SetTableNum("x", inf.x); 712 | SetTableNum("y", inf.y); 713 | SetTableNum("w", inf.w); 714 | SetTableNum("h", inf.h); 715 | } else { 716 | lua_pushnil(L); 717 | } 718 | return 1; 719 | } 720 | 721 | 722 | 723 | static int lwmc_get_win_list(lua_State*L) 724 | { 725 | XCtrl*ud=lwmc_check_obj(L); 726 | ulong size=0; 727 | Window*list=get_window_list(ud->dpy, &size); 728 | if (list) { 729 | unsigned long i; 730 | lua_newtable(L); 731 | for (i=0; i2) ? lua_toboolean(L,3) : 0; 765 | char*name=get_desktop_name(ud->dpy, desknum-1, force_utf8); 766 | lua_pushstring(L, name); 767 | if (name) { free(name); } 768 | return 1; 769 | } 770 | 771 | 772 | 773 | static int lwmc_set_showing_desktop(lua_State*L) 774 | { 775 | XCtrl*ud=lwmc_check_obj(L); 776 | luaL_argcheck(L, lua_gettop(L)>1, 2, "expected boolean"); 777 | set_showing_desktop(ud->dpy, lua_toboolean(L,2)); 778 | return 0; 779 | } 780 | 781 | 782 | 783 | static int lwmc_set_num_desks(lua_State*L) 784 | { 785 | XCtrl*ud=lwmc_check_obj(L); 786 | int n=luaL_checknumber(L,2); 787 | if (set_number_of_desktops(ud->dpy,n)) { 788 | lua_pushboolean(L,True); 789 | return 1; 790 | } else { 791 | return lwmc_failure(L,"set #desktops failed"); 792 | } 793 | } 794 | 795 | 796 | 797 | static int lwmc_set_curr_desk(lua_State*L) 798 | { 799 | XCtrl*ud=lwmc_check_obj(L); 800 | int n=luaL_checknumber(L,2); 801 | lua_pushboolean(L,set_current_desktop(ud->dpy,n-1)); 802 | return 1; 803 | } 804 | 805 | 806 | 807 | static int lwmc_set_desk_vport(lua_State*L) 808 | { 809 | XCtrl*ud=lwmc_check_obj(L); 810 | int x=luaL_checknumber(L,2); 811 | int y=luaL_checknumber(L,3); 812 | lua_pushboolean(L,change_viewport(ud->dpy,x,y)); 813 | return 1; 814 | } 815 | 816 | 817 | 818 | static int lwmc_set_desk_geom(lua_State*L) 819 | { 820 | XCtrl*ud=lwmc_check_obj(L); 821 | int x=luaL_checknumber(L,2); 822 | int y=luaL_checknumber(L,3); 823 | lua_pushboolean(L,change_geometry(ud->dpy,x,y)); 824 | return 1; 825 | } 826 | 827 | 828 | 829 | static int lwmc_get_num_desks(lua_State*L) 830 | { 831 | XCtrl*ud=lwmc_check_obj(L); 832 | lua_pushnumber(L,get_number_of_desktops(ud->dpy)); 833 | return 1; 834 | } 835 | 836 | 837 | 838 | static int lwmc_get_curr_desk(lua_State*L) 839 | { 840 | XCtrl*ud=lwmc_check_obj(L); 841 | lua_pushnumber(L,get_current_desktop(ud->dpy)+1); 842 | return 1; 843 | } 844 | 845 | 846 | 847 | static int lwmc_send_keys(lua_State*L) 848 | { 849 | XCtrl*ud=lwmc_check_obj(L); 850 | Window win=check_window(L,ud,2); 851 | const char*keys=luaL_checkstring(L,3); 852 | send_keystrokes(ud->dpy, win, keys); 853 | return 0; 854 | } 855 | 856 | 857 | 858 | static int lwmc_convert_locale(lua_State*L) 859 | { 860 | const char*src,*from_charset,*to_charset; 861 | char*dst; 862 | lwmc_check_obj(L); 863 | src=luaL_checkstring(L,2); 864 | from_charset=luaL_checkstring(L,3); 865 | to_charset=luaL_checkstring(L,4); 866 | dst=convert_locale(src,from_charset,to_charset); 867 | if (dst) { 868 | lua_pushstring(L,dst); 869 | free(dst); 870 | return 1; 871 | } else { 872 | return 0; 873 | } 874 | } 875 | 876 | 877 | 878 | static int lwmc_do_events(lua_State*L) 879 | { 880 | XCtrl*ud=lwmc_check_obj(L); 881 | int n=luaL_optnumber(L,2,1); 882 | int i; 883 | for (i=0; idpy,False); 886 | } 887 | return 0; 888 | } 889 | 890 | 891 | static int lwmc_get_selection(lua_State*L) 892 | { 893 | XCtrl*ud=lwmc_check_obj(L); 894 | const char*kind=luaL_optstring(L,2,"p"); 895 | Bool utf8=lua_gettop(L)>2?lua_toboolean(L,3):False; 896 | uchar*sel=get_selection(ud->dpy, kind[0], utf8); 897 | if (sel) { 898 | lua_pushstring(L, (char*)sel); 899 | free(sel); 900 | return 1; 901 | } 902 | return 0; 903 | } 904 | 905 | 906 | 907 | static int lwmc_set_selection(lua_State*L) 908 | { 909 | XCtrl*ud=lwmc_check_obj(L); 910 | const char*sel=luaL_checkstring(L,2); 911 | const char*kind=luaL_optstring(L,3,"p"); 912 | Bool utf8=lua_gettop(L)>3?lua_toboolean(L,3):False; 913 | set_selection(ud->dpy, kind[0], (char*)sel, utf8); 914 | return 0; 915 | } 916 | 917 | 918 | 919 | typedef struct { 920 | int i; 921 | lua_State *L; 922 | } cbdata; 923 | 924 | 925 | 926 | static int lwmc_listen_cb(int ev, Window id, void*p) 927 | { 928 | const char* evmap[] = { 929 | "w", /* XCTRL_EVENT_WINDOW_LIST_INSERT */ 930 | "x", /* XCTRL_EVENT_WINDOW_LIST_DELETE */ 931 | "a", /* XCTRL_EVENT_WINDOW_FOCUS_GAINED */ 932 | "i", /* XCTRL_EVENT_WINDOW_FOCUS_LOST */ 933 | "g", /* XCTRL_EVENT_WINDOW_MOVE_RESIZE */ 934 | "t", /* XCTRL_EVENT_WINDOW_TITLE */ 935 | "s", /* XCTRL_EVENT_WINDOW_STATE */ 936 | "d", /* XCTRL_EVENT_DESKTOP_SWITCH */ 937 | }; 938 | cbdata*c=(cbdata*)p; 939 | lua_rawgeti(c->L, LUA_REGISTRYINDEX, c->i); 940 | lua_pushstring(c->L, evmap[ev]); 941 | lua_pushnumber(c->L, (ev==XCTRL_EVENT_DESKTOP_SWITCH)?id+1:id); 942 | lua_pcall(c->L, 2, 1, 0); 943 | return lua_toboolean(c->L,-1); 944 | } 945 | 946 | 947 | 948 | static int lwmc_listen(lua_State*L) 949 | { 950 | cbdata c; 951 | XCtrl*ud=lwmc_check_obj(L); 952 | luaL_argcheck(L,lua_isfunction(L,2),2,"expected function"); 953 | c.L=L; 954 | c.i=luaL_ref(L,LUA_REGISTRYINDEX); 955 | event_loop(ud->dpy,lwmc_listen_cb,&c); 956 | luaL_unref(L,LUA_REGISTRYINDEX,c.i); 957 | return 0; 958 | } 959 | 960 | 961 | 962 | static const struct luaL_Reg lwmc_funcs[] = { 963 | {"new", lwmc_new}, 964 | {"get_win_list", lwmc_get_win_list}, 965 | {"get_win_title", lwmc_get_win_title}, 966 | {"set_win_title", lwmc_set_win_title}, 967 | {"get_win_class", lwmc_get_win_class}, 968 | {"activate_win", lwmc_activate_win}, 969 | {"iconify_win", lwmc_iconify_win}, 970 | {"set_win_state", lwmc_set_win_state}, 971 | {"set_win_geom", lwmc_set_win_geom}, 972 | {"get_win_geom", lwmc_get_win_geom}, 973 | {"get_win_frame", lwmc_get_win_frame}, 974 | {"get_win_type", lwmc_get_win_type}, 975 | {"set_win_decor", lwmc_set_win_decor}, 976 | {"get_active_win", lwmc_get_active_win}, 977 | {"pick_win", lwmc_pick_win}, 978 | {"root_win", lwmc_root_win}, 979 | {"set_desk_of_win", lwmc_set_desk_of_win}, 980 | {"get_desk_of_win", lwmc_get_desk_of_win}, 981 | {"get_pid_of_win", lwmc_get_pid_of_win}, 982 | {"close_win", lwmc_close_win}, 983 | {"get_win_client", lwmc_get_win_client}, 984 | {"get_num_desks", lwmc_get_num_desks}, 985 | {"set_num_desks", lwmc_set_num_desks}, 986 | {"get_curr_desk", lwmc_get_curr_desk}, 987 | {"set_curr_desk", lwmc_set_curr_desk}, 988 | {"get_display", lwmc_get_display}, 989 | {"get_wm_win", lwmc_get_wm_win}, 990 | {"get_wm_name", lwmc_get_wm_name}, 991 | {"get_wm_class", lwmc_get_wm_class}, 992 | {"get_wm_pid", lwmc_get_wm_pid}, 993 | {"get_desk_name", lwmc_get_desk_name}, 994 | {"get_workarea", lwmc_get_workarea}, 995 | {"get_desk_geom", lwmc_get_desk_geom}, 996 | {"set_desk_vport", lwmc_set_desk_vport}, 997 | {"set_desk_geom", lwmc_set_desk_geom}, 998 | {"set_showing_desk",lwmc_set_showing_desktop}, 999 | {"get_showing_desk",lwmc_get_showing_desktop}, 1000 | {"send_keys", lwmc_send_keys}, 1001 | {"do_events", lwmc_do_events}, 1002 | {"convert_locale", lwmc_convert_locale}, 1003 | {"get_selection", lwmc_get_selection}, 1004 | {"set_selection", lwmc_set_selection}, 1005 | {"listen", lwmc_listen}, 1006 | {NULL,NULL} 1007 | }; 1008 | 1009 | 1010 | int luaopen_xctrl(lua_State*L); 1011 | 1012 | int luaopen_xctrl(lua_State*L) 1013 | { 1014 | luaL_newmetatable(L, XCTRL_META_NAME); 1015 | lua_pushstring(L, "__index"); 1016 | lua_pushvalue(L, -2); 1017 | lua_settable(L, -3); 1018 | 1019 | lua_pushstring(L,"__gc"); 1020 | lua_pushcfunction(L,lwmc_gc); 1021 | lua_rawset(L,-3); 1022 | 1023 | lua_pushstring(L,"__tostring"); 1024 | lua_pushcfunction(L,lwmc_tostring); 1025 | lua_rawset(L,-3); 1026 | 1027 | #if LUA_VERSION_NUM < 502 1028 | luaL_register(L, NULL, &lwmc_funcs[1]); 1029 | luaL_register(L, XCTRL_META_NAME, lwmc_funcs); 1030 | #else 1031 | luaL_setfuncs(L,lwmc_funcs,0); 1032 | #endif 1033 | return 1; 1034 | } 1035 | 1036 | -------------------------------------------------------------------------------- /src/xctrl.c: -------------------------------------------------------------------------------- 1 | /* 2 | X11 control library, based largely on Tomas Styblo's "wmctrl". 3 | 4 | The original wmctrl is Copyright (C) 2003 Tomas Styblo 5 | and is available from http://tripie.sweb.cz/utils/wmctrl/ 6 | 7 | This library derived in 2010 by Jeffrey Pohlmeyer 8 | 9 | The charset conversion routines were derived from Text-Unaccent-1.08/unac.c 10 | Copyright (C) 2000, 2001, 2002, 2003 Loic Dachary 11 | Text-Unaccent is available from http://search.cpan.org/~ldachary/ 12 | 13 | The clipboard and selection routines were derived from xclip, 14 | a command line interface to X server selections. 15 | Copyright (C) 2001 Kim Saunders 16 | Copyright (C) 2007-2008 Peter Astrand 17 | xclip is available from http://sourceforge.net/projects/xclip/ 18 | 19 | All source code mentioned above is released under the following terms: 20 | 21 | This program is free software, released under the GNU General Public 22 | License. You may redistribute and/or modify this program under the terms 23 | of that license as published by the Free Software Foundation; either 24 | version 2 of the License, or (at your option) any later version. 25 | 26 | This program is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | GNU General Public License for more details. 30 | 31 | To get a copy of the GNU General Puplic License, write to the 32 | Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 33 | 34 | */ 35 | 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include 49 | #include 50 | #include "xctrl.h" 51 | 52 | 53 | #define DefRootWin DefaultRootWindow(disp) 54 | #define GetUTF8Atom() XInternAtom(disp, "UTF8_STRING", False) 55 | #define sfree(p) if (p) { free(p); } 56 | 57 | #define DEFAULT_CHARSET "ISO_8859-1" 58 | 59 | XCTRL_API char* default_charset=DEFAULT_CHARSET; 60 | 61 | XCTRL_API Bool envir_utf8; 62 | 63 | 64 | XCTRL_API char* convert_locale(const char*asrc,const char*from, const char*to) 65 | { 66 | iconv_t cd; 67 | size_t out_remain; 68 | size_t out_size; 69 | char* psrc=(char*)asrc; 70 | char* out_base; 71 | char* pdst; 72 | Bool from_utf16 = (strcmp("UTF-16BE", from)==0)||(strcmp("UTF-16", from)==0); 73 | const char space[] = { 0x00, 0x20 }; 74 | size_t srclen=asrc?strlen(asrc):0; 75 | out_size = srclen > 0 ? srclen : 1024; 76 | out_base = (char*)malloc(out_size + 1); 77 | if(!out_base) { return NULL; } 78 | out_remain = out_size; 79 | pdst = out_base; 80 | if ((cd = iconv_open(to, from)) == (iconv_t)-1) { 81 | free(out_base); 82 | return NULL; 83 | } 84 | do { 85 | if (iconv(cd, &psrc, &srclen, &pdst, &out_remain) == (size_t)-1) { 86 | switch(errno) { 87 | case EILSEQ: { 88 | if(from_utf16) { 89 | char* tmp = (char*)space; 90 | size_t tmp_length = 2; 91 | if(iconv(cd, &tmp, &tmp_length, &pdst, &out_remain) == (size_t)-1) { 92 | if (errno != E2BIG) { 93 | free(out_base); 94 | return NULL; 95 | } else { 96 | /* fall thru to E2BIG below */ 97 | } 98 | } else { /* The bad char was replaced with SPACE, so skip it. */ 99 | psrc += 2; 100 | srclen -= 2; 101 | break; 102 | } 103 | } else { 104 | free(out_base); 105 | return NULL; 106 | } 107 | } 108 | case E2BIG: { 109 | int length = pdst - out_base; 110 | out_size *= 2; 111 | out_base = (char*)realloc(out_base, out_size + 1); 112 | if (!out_base) { return NULL; } 113 | pdst = out_base + length; 114 | out_remain = out_size - length; 115 | break; 116 | } 117 | default: { 118 | free(out_base); 119 | return NULL; 120 | } 121 | } 122 | } 123 | } while(srclen > 0); 124 | iconv_close(cd); 125 | pdst[0]='\0'; 126 | return out_base; 127 | } 128 | 129 | 130 | 131 | XCTRL_API char *locale_to_utf8(const char*src) { 132 | return convert_locale( src, default_charset, "UTF-8"); 133 | } 134 | 135 | 136 | 137 | XCTRL_API char *utf8_to_locale(const char*src) { 138 | return convert_locale(src, "UTF-8", default_charset); 139 | } 140 | 141 | 142 | 143 | static char *ascii_strup(const char*src) 144 | { 145 | if (src) { 146 | char*tmp=strdup(src); 147 | char*p; 148 | for (p=tmp; *p; p++) { if ((signed char)(*p) > 0 ) { *p=toupper(*p); } } 149 | return tmp; 150 | } else { 151 | return NULL; 152 | } 153 | } 154 | 155 | 156 | 157 | static Bool is_envar_utf8(const char*envar) 158 | { 159 | char*enval=getenv(envar); 160 | if (enval) { 161 | Bool rv; 162 | enval=ascii_strup(enval); 163 | rv=strstr(enval, "UTF8") || strstr(enval, "UTF-8"); 164 | sfree(enval); 165 | return rv; 166 | } else { 167 | return False; 168 | } 169 | } 170 | 171 | 172 | 173 | XCTRL_API void init_charset(Bool force_utf8, char*charset) 174 | { 175 | envir_utf8 = force_utf8?True:is_envar_utf8("LANG") || is_envar_utf8("LC_CTYPE"); 176 | default_charset = charset ? charset : getenv("CHARSET") ? getenv("CHARSET") : DEFAULT_CHARSET; 177 | } 178 | 179 | 180 | 181 | static Bool client_msg(Display *disp, Window win, char *msg, ulong d0, ulong d1, ulong d2, ulong d3, ulong d4) { 182 | XEvent event; 183 | long mask = SubstructureRedirectMask | SubstructureNotifyMask; 184 | event.type = ClientMessage; 185 | event.xclient.type = ClientMessage; 186 | event.xclient.serial = 0; 187 | event.xclient.send_event = True; 188 | event.xclient.message_type = XInternAtom(disp, msg, False); 189 | event.xclient.window = win; 190 | event.xclient.format = 32; 191 | event.xclient.data.l[0] = d0; 192 | event.xclient.data.l[1] = d1; 193 | event.xclient.data.l[2] = d2; 194 | event.xclient.data.l[3] = d3; 195 | event.xclient.data.l[4] = d4; 196 | return XSendEvent(disp, DefRootWin, False, mask, &event)?True:False; 197 | } 198 | 199 | 200 | 201 | static char *get_output_str(char*str, Bool is_utf8) { 202 | char *out; 203 | if (!str) { return NULL; } 204 | if (envir_utf8) { 205 | if (is_utf8) { 206 | out = strdup(str); 207 | } else { 208 | out=locale_to_utf8(str); 209 | if (!out) { out = strdup(str); } 210 | } 211 | } else { 212 | if (is_utf8) { 213 | out = utf8_to_locale(str); 214 | if (!out) { out = strdup(str); } 215 | } else { 216 | out = strdup(str); 217 | } 218 | } 219 | return out; 220 | } 221 | 222 | 223 | 224 | #define get_uprop(wn,pn,sz) (ulong*)get_prop(disp, wn, XA_CARDINAL, pn, sz) 225 | 226 | static char *get_prop(Display*d, Window w, Atom type, const char*name, ulong*count) { 227 | Atom a=XInternAtom(d, name, False); 228 | Atom ret_type; 229 | int format=0; 230 | ulong nitems=0; 231 | ulong after=0; 232 | uchar *retp=NULL; 233 | ulong total_bytes=0; 234 | char*all=NULL; 235 | XGetWindowProperty(d,w,a,0,0,False,AnyPropertyType,&ret_type,&format,&nitems,&after,&retp); 236 | if (retp) { XFree(retp); } 237 | if (type!=ret_type) { 238 | if (count) { *count=0; } 239 | return NULL; 240 | } 241 | all=(char*)malloc(1); 242 | do { 243 | XGetWindowProperty(d,w,a,total_bytes/4,1024,False,ret_type,&ret_type,&format,&nitems,&after,&retp); 244 | if (retp) { 245 | uint chunk_bytes = (format/8)*nitems; 246 | if (format == 32) { chunk_bytes *= sizeof(long) / 4; } /* Fix x86_64 handling 32-bit data */ 247 | all = (char*)realloc(all,chunk_bytes+total_bytes+1); 248 | memcpy(&(all[total_bytes]), retp, chunk_bytes); 249 | total_bytes+=chunk_bytes; 250 | XFree(retp); 251 | } 252 | } while (after!=0); 253 | if (total_bytes) { 254 | all[total_bytes] = '\0'; 255 | if (count) { 256 | *count=total_bytes/(format/8); 257 | if (format == 32) { *count /= sizeof(long) / 4; } /* Fix x86_64 handling 32-bit data */ 258 | } 259 | return(all); 260 | } else { 261 | free(all); 262 | return NULL; 263 | } 264 | } 265 | 266 | 267 | 268 | static char* get_prop_utf8(Display*disp, Window win, const char*what) 269 | { 270 | Bool name_is_utf8 = True; 271 | char*result=NULL; 272 | char*tmp=get_prop(disp, win, GetUTF8Atom(), what, NULL); 273 | if (!tmp) { 274 | name_is_utf8 = False; 275 | tmp = get_prop(disp, win, XA_STRING, what, NULL); 276 | } 277 | result=get_output_str(tmp, name_is_utf8); 278 | sfree(tmp); 279 | return result; 280 | } 281 | 282 | 283 | /* 284 | Save the value of p, free p, and return the 285 | saved value, or return ifnull if p is NULL. 286 | */ 287 | static ulong ptr_to_ulong(ulong*p, ulong ifnull) 288 | { 289 | if (p) { 290 | ulong rv=*p; 291 | free(p); 292 | return rv; 293 | } else { 294 | return ifnull; 295 | } 296 | } 297 | 298 | 299 | 300 | XCTRL_API Window supporting_wm_check(Display*disp) 301 | { 302 | Window *p=(Window*)get_prop(disp, DefRootWin, XA_WINDOW, "_NET_SUPPORTING_WM_CHECK", NULL); 303 | if (!p) { 304 | p=(Window*)get_prop(disp, DefRootWin, XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK", NULL); 305 | } 306 | return (Window)ptr_to_ulong((ulong*)p,0); 307 | } 308 | 309 | 310 | 311 | XCTRL_API char* get_wm_name(Display*disp) 312 | { 313 | Window win = supporting_wm_check(disp); 314 | return win?get_prop_utf8(disp, win, "_NET_WM_NAME"):NULL; 315 | } 316 | 317 | 318 | 319 | XCTRL_API char* get_wm_class(Display*disp) 320 | { 321 | Window win = supporting_wm_check(disp); 322 | return win?get_prop_utf8(disp, win, "WM_CLASS"):NULL; 323 | } 324 | 325 | 326 | 327 | XCTRL_API ulong get_wm_pid(Display*disp) 328 | { 329 | Window win = supporting_wm_check(disp); 330 | if (win) { 331 | ulong *wm_pid = get_uprop(win, "_NET_WM_PID", NULL); 332 | return ptr_to_ulong(wm_pid, 0); 333 | } else { 334 | return 0; 335 | } 336 | } 337 | 338 | 339 | 340 | XCTRL_API int get_showing_desktop(Display*disp) 341 | { 342 | ulong *p=get_uprop(DefRootWin, "_NET_SHOWING_DESKTOP", NULL); 343 | return ptr_to_ulong(p,-1); 344 | } 345 | 346 | 347 | 348 | XCTRL_API int set_showing_desktop(Display*disp, ulong state) 349 | { 350 | return client_msg(disp, DefRootWin, "_NET_SHOWING_DESKTOP", state, 0, 0, 0, 0); 351 | } 352 | 353 | 354 | 355 | XCTRL_API int change_viewport(Display*disp, ulong x, ulong y) { 356 | return client_msg(disp, DefRootWin, "_NET_DESKTOP_VIEWPORT", x, y, 0, 0, 0); 357 | } 358 | 359 | 360 | 361 | XCTRL_API int change_geometry(Display*disp, ulong x, ulong y) { 362 | return client_msg(disp, DefRootWin, "_NET_DESKTOP_GEOMETRY", x, y, 0, 0, 0); 363 | } 364 | 365 | 366 | 367 | XCTRL_API int set_number_of_desktops(Display*disp, ulong n) 368 | { 369 | return client_msg(disp, DefRootWin, "_NET_NUMBER_OF_DESKTOPS", n, 0, 0, 0, 0); 370 | } 371 | 372 | 373 | 374 | XCTRL_API long get_number_of_desktops(Display*disp) 375 | { 376 | ulong *p; 377 | p = get_uprop(DefRootWin, "_NET_NUMBER_OF_DESKTOPS", NULL); 378 | if (!p) { 379 | p = get_uprop(DefRootWin, "_WIN_WORKSPACE_COUNT", NULL); 380 | } 381 | return ptr_to_ulong(p,-1); 382 | } 383 | 384 | 385 | 386 | XCTRL_API int set_current_desktop(Display*disp, ulong target) { 387 | return client_msg(disp, DefRootWin, "_NET_CURRENT_DESKTOP", target, 0, 0, 0, 0); 388 | } 389 | 390 | 391 | 392 | XCTRL_API long get_current_desktop(Display*disp) 393 | { 394 | ulong *p; 395 | p = get_uprop(DefRootWin, "_NET_CURRENT_DESKTOP", NULL); 396 | if (!p) { 397 | p = get_uprop(DefRootWin, "_WIN_WORKSPACE", NULL); 398 | } 399 | return (signed long)ptr_to_ulong(p,-1); 400 | } 401 | 402 | 403 | 404 | static void replace_prop(Display*disp, Window win, Atom what, Atom type, char*newval) 405 | { 406 | XChangeProperty(disp,win,what,type,8,PropModeReplace,(unsigned char*)newval,strlen(newval)); 407 | } 408 | 409 | 410 | 411 | XCTRL_API void set_window_title(Display*disp, Window win, const char*title, char mode) { 412 | char *title_utf8; 413 | char *title_local; 414 | Atom utf8_atom = GetUTF8Atom(); 415 | if (envir_utf8) { 416 | title_utf8 = strdup(title); 417 | title_local = NULL; 418 | } else { 419 | title_utf8 = locale_to_utf8(title); 420 | if (!title_utf8) { title_utf8 = strdup(title); } 421 | title_local = strdup(title); 422 | } 423 | if (mode == 'T' || mode == 'N') { 424 | if (title_local) { 425 | replace_prop(disp, win, XA_WM_NAME, XA_STRING,title_local); 426 | } else { 427 | XDeleteProperty(disp, win, XA_WM_NAME); 428 | } 429 | replace_prop(disp, win, XInternAtom(disp,"_NET_WM_NAME",False), utf8_atom, title_utf8); 430 | } 431 | if (mode == 'T' || mode == 'I') { 432 | if (title_local) { 433 | replace_prop(disp, win, XA_WM_ICON_NAME, XA_STRING, title_local); 434 | } else { 435 | XDeleteProperty(disp, win, XA_WM_ICON_NAME); 436 | } 437 | replace_prop(disp, win, XInternAtom(disp, "_NET_WM_ICON_NAME", False), utf8_atom, title_utf8); 438 | } 439 | sfree(title_utf8); 440 | sfree(title_local); 441 | } 442 | 443 | 444 | 445 | XCTRL_API int send_window_to_desktop(Display*disp, Window win, int desktop) { 446 | return client_msg(disp, win, "_NET_WM_DESKTOP", (ulong)desktop, 0, 0, 0, 0); 447 | } 448 | 449 | 450 | 451 | XCTRL_API Bool activate_window(Display*disp, Window win, Bool switch_desk) { 452 | if (switch_desk) { 453 | long desktop = get_desktop_of_window(disp,win); 454 | if (desktop>=0) { 455 | if (desktop!=get_current_desktop(disp)) { 456 | set_current_desktop(disp, desktop); 457 | } 458 | } 459 | } 460 | client_msg(disp, win, "_NET_ACTIVE_WINDOW", 2, 0, 0, 0, 0); 461 | XSetInputFocus(disp, win, RevertToNone, CurrentTime); 462 | XMapRaised(disp, win); 463 | return True; 464 | } 465 | 466 | 467 | 468 | XCTRL_API Bool iconify_window(Display*disp, Window win) 469 | { 470 | return XIconifyWindow(disp, win, DefaultScreen(disp))?True:False; 471 | } 472 | 473 | 474 | 475 | XCTRL_API Window get_active_window(Display*disp) { 476 | Window*win = (Window*)get_prop(disp, DefRootWin, XA_WINDOW, "_NET_ACTIVE_WINDOW", NULL); 477 | return (Window) ptr_to_ulong((ulong*)win,0); 478 | } 479 | 480 | 481 | 482 | XCTRL_API int close_window(Display*disp, Window win) { 483 | return client_msg(disp, win, "_NET_CLOSE_WINDOW", 0, 0, 0, 0, 0); 484 | } 485 | 486 | 487 | 488 | static ulong wm_state_atom(Display*disp, const char*prop) { 489 | Atom atom=0; 490 | char tmp_prop[64]; 491 | char*p; 492 | memset(tmp_prop,0,sizeof(tmp_prop)); 493 | strcpy(tmp_prop, "_NET_WM_STATE_"); 494 | strncat(tmp_prop,prop,(sizeof(tmp_prop)-strlen(tmp_prop))-1); 495 | for (p=tmp_prop; *p; p++) { 496 | if (((signed char)*p)>0) { *p=toupper(*p); } 497 | } 498 | atom = XInternAtom(disp, tmp_prop, False); 499 | return (ulong)atom; 500 | } 501 | 502 | 503 | 504 | XCTRL_API int set_window_state(Display*disp, Window win, ulong action, const char*p1, const char*p2) 505 | { 506 | ulong xa_prop1=p1?wm_state_atom(disp,p1):0; 507 | ulong xa_prop2=p2?wm_state_atom(disp,p2):0; 508 | return client_msg(disp,win,"_NET_WM_STATE",action,xa_prop1,xa_prop2, 0, 0); 509 | } 510 | 511 | 512 | 513 | XCTRL_API Bool wm_supports(Display*disp, const char*prop) { 514 | Atom xa_prop = XInternAtom(disp, prop, False); 515 | ulong size=0; 516 | int i; 517 | Atom *list=(Atom*)get_prop(disp, DefRootWin, XA_ATOM, "_NET_SUPPORTED", &size); 518 | if (!list) { return False; } 519 | for (i = 0; i < size; i++) { 520 | if (list[i] == xa_prop) { 521 | free(list); 522 | return True; 523 | } 524 | } 525 | free(list); 526 | return False; 527 | } 528 | 529 | 530 | XCTRL_API char*get_window_type(Display*disp, Window win) 531 | { 532 | static const char*shortnames[]={ 533 | "desktop", 534 | "dock", 535 | "toolbar", 536 | "menu", 537 | "utility", 538 | "splash", 539 | "dialog", 540 | "normal" 541 | }; 542 | const char*type="unsupported"; 543 | if (wm_supports(disp,"_NET_WM_WINDOW_TYPE")) { 544 | ulong size=0; 545 | Atom*atom=NULL; 546 | static Atom wintypes[8]={0,0,0,0,0,0,0,0}; 547 | if (wintypes[0]==0) { 548 | static char*netnames[]={ 549 | "_NET_WM_WINDOW_TYPE_DESKTOP", 550 | "_NET_WM_WINDOW_TYPE_DOCK", 551 | "_NET_WM_WINDOW_TYPE_TOOLBAR", 552 | "_NET_WM_WINDOW_TYPE_MENU", 553 | "_NET_WM_WINDOW_TYPE_UTILITY", 554 | "_NET_WM_WINDOW_TYPE_SPLASH", 555 | "_NET_WM_WINDOW_TYPE_DIALOG", 556 | "_NET_WM_WINDOW_TYPE_NORMAL" 557 | }; 558 | if (!XInternAtoms(disp,netnames,8,False,wintypes)) { 559 | return strdup(type); 560 | } 561 | } 562 | type=""; 563 | atom=(Atom*)get_prop(disp, win,XA_ATOM,"_NET_WM_WINDOW_TYPE",&size); 564 | if (atom) { 565 | int i; 566 | for (i=0; i<8;i++) { 567 | if (*atom==wintypes[i]) { 568 | type=shortnames[i]; 569 | break; 570 | } 571 | } 572 | sfree(atom); 573 | } 574 | if (type[0]=='\0') { 575 | Window tw=0; 576 | if (XGetTransientForHint(disp, win, &tw)) { 577 | type="dialog"; 578 | } else { type="normal"; } 579 | } 580 | } 581 | return strdup(type); 582 | } 583 | 584 | 585 | 586 | typedef struct { 587 | unsigned long flags; 588 | unsigned long functions; 589 | unsigned long decorations; 590 | long inputmode; 591 | unsigned long status; 592 | } MotifHints; 593 | 594 | 595 | 596 | XCTRL_API void set_window_mwm_hints(Display*disp, Window win, ulong flags, ulong funcs, ulong decors, ulong imode) 597 | { 598 | Atom atom=0; 599 | MotifHints hints; 600 | hints.flags=flags; 601 | hints.functions=funcs; 602 | hints.decorations=decors; 603 | hints.inputmode=imode; 604 | hints.status=0; 605 | if (!atom) { atom=XInternAtom(disp, "_MOTIF_WM_HINTS", False); } 606 | XChangeProperty(disp,win,atom,atom,32,PropModeReplace,(unsigned char*)&hints,4); 607 | } 608 | 609 | 610 | 611 | XCTRL_API void get_window_geom(Display*disp, Window win, Geometry*geom) 612 | { 613 | int x, y; 614 | unsigned int bw, depth; 615 | Window root; 616 | memset(geom,0,sizeof(Geometry)); 617 | XGetGeometry(disp, win, &root, &x, &y, &geom->w, &geom->h, &bw, &depth); 618 | XTranslateCoordinates(disp, win, root, x, y, &geom->x, &geom->y, &root); 619 | } 620 | 621 | 622 | 623 | XCTRL_API Bool get_window_frame(Display*disp, Window win, long*left, long*right, long*top, long*bottom) 624 | { 625 | if (wm_supports(disp, "_NET_FRAME_EXTENTS")) { 626 | ulong size=0; 627 | ulong*extents=get_uprop(win,"_NET_FRAME_EXTENTS",&size); 628 | *left=*right=*top=*bottom=-1; 629 | if (extents) { 630 | *left=extents[0]; 631 | *right=extents[1]; 632 | *top=extents[2]; 633 | *bottom=extents[3]; 634 | sfree(extents); 635 | return True; 636 | } 637 | } 638 | return False; 639 | } 640 | 641 | 642 | 643 | XCTRL_API int set_window_geom(Display*disp, Window win, long grav, long flags, long x, long y, long w, long h) 644 | { 645 | if (wm_supports(disp, "_NET_MOVERESIZE_WINDOW")) { 646 | return client_msg( disp, win, "_NET_MOVERESIZE_WINDOW", 647 | grav|flags, (ulong)x, (ulong)y, (ulong)w, (ulong)h); 648 | } else { 649 | Bool move=False; 650 | Bool size=False; 651 | Geometry geom; 652 | get_window_geom(disp,win,&geom); 653 | if (flags&(XCTRL_GEOM_USE_X|XCTRL_GEOM_USE_Y)) { 654 | move=True; 655 | if (!(flags&XCTRL_GEOM_USE_X)) { x=geom.x; } 656 | if (!(flags&XCTRL_GEOM_USE_Y)) { y=geom.y; } 657 | } 658 | if (flags&(XCTRL_GEOM_USE_W|XCTRL_GEOM_USE_H)) { 659 | size=True; 660 | if (!(flags&XCTRL_GEOM_USE_W)) { w=geom.w; } 661 | if (!(flags&XCTRL_GEOM_USE_H)) { h=geom.h; } 662 | } 663 | if (size&&move) { 664 | XMoveResizeWindow(disp, win, x, y, w, h); 665 | } else if (size) { 666 | XResizeWindow(disp, win, w, h); 667 | } else if (move) { 668 | XMoveWindow(disp, win, x, y); 669 | } 670 | return True; 671 | } 672 | } 673 | 674 | 675 | 676 | XCTRL_API Window *get_window_list(Display*disp, ulong*size) 677 | { 678 | Window *client_list; 679 | client_list = (Window*)get_prop(disp, DefRootWin, XA_WINDOW, "_NET_CLIENT_LIST", size); 680 | if (!client_list) { 681 | client_list = (Window*)get_prop(disp, DefRootWin, XA_CARDINAL, "_WIN_CLIENT_LIST", size); 682 | } 683 | return client_list; 684 | } 685 | 686 | 687 | 688 | XCTRL_API char *get_window_class(Display*disp, Window win) 689 | { 690 | char *class_utf8; 691 | char *wm_class; 692 | ulong size=0; 693 | wm_class = get_prop(disp, win, XA_STRING, "WM_CLASS", &size); 694 | if (wm_class) { 695 | char *p_0 = strchr(wm_class, '\0'); 696 | if (wm_class + size - 1 > p_0) { *(p_0) = '.'; } 697 | class_utf8 = locale_to_utf8(wm_class); 698 | } else { 699 | class_utf8 = NULL; 700 | } 701 | sfree(wm_class); 702 | return class_utf8; 703 | } 704 | 705 | 706 | 707 | XCTRL_API char *get_window_title(Display*disp, Window win) 708 | { 709 | char *wm_name = get_prop(disp, win, GetUTF8Atom(), "_NET_WM_NAME", NULL); 710 | if (wm_name) { 711 | return wm_name; 712 | } else { 713 | char *title_utf8 = NULL; 714 | wm_name = get_prop(disp, win, XA_STRING, "WM_NAME", NULL); 715 | if (wm_name) { 716 | title_utf8 = locale_to_utf8(wm_name); 717 | free(wm_name); 718 | } else { 719 | title_utf8 = NULL; 720 | } 721 | return title_utf8; 722 | } 723 | } 724 | 725 | 726 | 727 | static void pass_click_to_client(Display*disp, Window root, XEvent*event, int mask, Cursor cursor) 728 | { 729 | usleep(1000); 730 | XSync(disp,False); 731 | event->xbutton.window=event->xbutton.subwindow; 732 | XSendEvent(disp, event->xbutton.subwindow, True, mask, event); 733 | usleep(1000); 734 | XSync(disp,False); 735 | } 736 | 737 | 738 | #define GrabMouse() ( \ 739 | XGrabPointer(disp,root,False,ButtonPressMask|ButtonReleaseMask, \ 740 | GrabModeSync,GrabModeAsync,root,cursor,CurrentTime) == GrabSuccess ) 741 | 742 | 743 | /* Routine to let user select a window using the mouse adapted from xfree86. */ 744 | XCTRL_API Window select_window(Display*disp, int button) 745 | { 746 | Cursor cursor; 747 | XEvent event; 748 | Window target_win = None; 749 | Window root = DefaultRootWindow(disp); 750 | int buttons = 0; 751 | int dumi; 752 | unsigned int dum; 753 | cursor = XCreateFontCursor(disp, XC_crosshair); 754 | if (!GrabMouse()) { 755 | XFreeCursor(disp,cursor); 756 | return 0; 757 | } 758 | while ((target_win == None) || (buttons != 0)) { 759 | XAllowEvents(disp, SyncPointer, CurrentTime); 760 | XWindowEvent(disp, root, ButtonPressMask|ButtonReleaseMask, &event); 761 | switch (event.type) { 762 | case ButtonPress: 763 | if ((button==-1)||(event.xbutton.button==button)) { 764 | if (target_win == None) { 765 | target_win = event.xbutton.subwindow; /* window selected */ 766 | if (target_win == None) target_win = root; 767 | } 768 | } else { 769 | pass_click_to_client(disp,root,&event,ButtonPressMask,cursor); 770 | } 771 | buttons++; 772 | break; 773 | case ButtonRelease: 774 | if (buttons > 0) { buttons--; } 775 | if ((button!=-1)&&(event.xbutton.button!=button)) { 776 | pass_click_to_client(disp,root,&event,ButtonReleaseMask,cursor); 777 | } 778 | break; 779 | } 780 | } 781 | XUngrabPointer(disp, CurrentTime); 782 | if (XGetGeometry(disp,target_win,&root,&dumi,&dumi,&dum,&dum,&dum,&dum)&&target_win!=root) { 783 | target_win = XmuClientWindow(disp, target_win); 784 | } 785 | return(target_win); 786 | } 787 | 788 | 789 | 790 | XCTRL_API char* get_desktop_name(Display*disp, int desknum, Bool force_utf8) 791 | { 792 | char *name_list = NULL; 793 | ulong name_list_size = 0; 794 | char *name = NULL; 795 | Bool names_are_utf8 = True; 796 | Window root = DefRootWin; 797 | char*rv=NULL; 798 | if (desknum<0) { return NULL; } 799 | if (force_utf8) { 800 | name_list = get_prop(disp, root, GetUTF8Atom(), "_NET_DESKTOP_NAMES", &name_list_size); 801 | } 802 | if (!name_list) { 803 | names_are_utf8 = False; 804 | name_list = get_prop(disp, root, XA_STRING, "_WIN_WORKSPACE_NAMES", &name_list_size); 805 | } 806 | if (name_list) { 807 | if (desknum>0) { 808 | int id=1; 809 | char*p=name_list; 810 | do { 811 | p=strchr(p,'\0')+1; 812 | if (id==desknum) { 813 | name=p; 814 | break; 815 | } 816 | id++; 817 | } while (p<(name_list+name_list_size)); 818 | } else { name=name_list; } 819 | if (name && *name) { rv = get_output_str(name, names_are_utf8); } 820 | free(name_list); 821 | } 822 | return rv; 823 | } 824 | 825 | 826 | 827 | XCTRL_API int get_workarea_geom(Display*disp, Geometry*geom, int desknum) 828 | { 829 | ulong *wkarea = NULL; 830 | ulong wkarea_size = 0; 831 | int rv=0; 832 | if (desknum<0) { return 0; } 833 | if (get_number_of_desktops(disp)<=desknum) { return 0; } 834 | wkarea = get_uprop(DefRootWin, "_NET_WORKAREA", &wkarea_size); 835 | if (!wkarea) { 836 | wkarea = get_uprop(DefRootWin, "_WIN_WORKAREA", &wkarea_size); 837 | } 838 | if (wkarea && wkarea_size > 0) { 839 | if (wkarea_size == (4*sizeof(*wkarea))) { 840 | geom->x=wkarea[0]; 841 | geom->y=wkarea[1]; 842 | geom->w=wkarea[2]; 843 | geom->h=wkarea[3]; 844 | rv=1; 845 | } else { 846 | if (desknum < (wkarea_size/4)) { 847 | geom->x=wkarea[desknum*4]; 848 | geom->y=wkarea[desknum*4+1]; 849 | geom->w=wkarea[desknum*4+2]; 850 | geom->h=wkarea[desknum*4+3]; 851 | rv=1; 852 | } 853 | } 854 | sfree(wkarea); 855 | } 856 | return rv; 857 | } 858 | 859 | 860 | 861 | XCTRL_API int get_desktop_geom(Display*disp, int desknum, Geometry*geom) 862 | { 863 | ulong *desk_geom = NULL; 864 | ulong geom_size = 0; 865 | ulong *vport = NULL; 866 | ulong vport_size = 0; 867 | int rv=0; 868 | if (desknum<0) { return 0; } 869 | if (get_number_of_desktops(disp)<=desknum) { return 0; } 870 | desk_geom = get_uprop(DefRootWin, "_NET_DESKTOP_GEOMETRY", &geom_size); 871 | vport = get_uprop(DefRootWin, "_NET_DESKTOP_VIEWPORT", &vport_size); 872 | if (desk_geom && geom_size > 0) { 873 | if (geom_size == 2 * sizeof(*desk_geom)) { 874 | geom->w=desk_geom[0]; 875 | geom->h=desk_geom[1]; 876 | } else { 877 | geom->w=desk_geom[desknum*2]; 878 | geom->h=desk_geom[desknum*2+1]; 879 | } 880 | rv=1; 881 | } 882 | if (vport && vport_size > 0) { 883 | if (vport_size == 2 * sizeof(*vport)) { 884 | geom->x=vport[0]; 885 | geom->y=vport[1]; 886 | } else { 887 | if (desknum < vport_size / sizeof(*vport) / 2) { 888 | geom->x=vport[desknum*2]; 889 | geom->y=vport[desknum*2+1]; 890 | } 891 | } 892 | rv=1; 893 | } 894 | sfree(desk_geom); 895 | sfree(vport); 896 | return rv; 897 | } 898 | 899 | 900 | 901 | XCTRL_API long get_desktop_of_window(Display*disp, Window win) 902 | { 903 | ulong *p = get_uprop(win, "_NET_WM_DESKTOP", NULL); 904 | if (!p) { 905 | p = get_uprop(win, "_WIN_WORKSPACE", NULL); 906 | } 907 | return (signed long)ptr_to_ulong(p,-2); 908 | } 909 | 910 | 911 | 912 | XCTRL_API ulong get_win_pid(Display*disp, Window win) 913 | { 914 | ulong *p=get_uprop(win, "_NET_WM_PID", NULL); 915 | return ptr_to_ulong(p,0); 916 | } 917 | 918 | 919 | XCTRL_API char*get_client_machine(Display *disp, Window win) 920 | { 921 | return get_prop(disp, win, XA_STRING, "WM_CLIENT_MACHINE", NULL); 922 | } 923 | 924 | 925 | 926 | /* 927 | Send "fake" keystroke events to an X window. 928 | Adapted from the (public domain) example by by Adam Pierce -- 929 | http://www.doctort.org/adam/nerd-notes/x11-fake-keypress-event.html 930 | */ 931 | XCTRL_API void send_keystrokes(Display*disp, Window win, const char*keys) 932 | { 933 | Bool escaped=0; 934 | const char* numkeys_upper="~!@#$%^&*()_+|"; 935 | const char* numkeys_lower="`1234567890-=\\"; 936 | int navkeys[]={XK_Insert,XK_End,XK_Down,XK_Page_Down,XK_Left,-1,XK_Right,XK_Home,XK_Up,XK_Page_Up}; 937 | int funkeys[]={XK_F1,XK_F2,XK_F3,XK_F4,XK_F5,XK_F6,XK_F7,XK_F8,XK_F9,XK_F10,XK_F11,XK_F12}; 938 | unsigned const char*p; 939 | char*n; 940 | XEvent ev; 941 | memset(&ev.xkey,0,sizeof(XKeyEvent)); 942 | ev.xkey.subwindow=None; 943 | ev.xkey.serial=1; 944 | ev.xkey.display=disp; 945 | ev.xkey.window=win; 946 | ev.xkey.root=DefRootWin; 947 | ev.xkey.same_screen=1; 948 | for (p=(unsigned const char*)keys; *p; p++) { 949 | int c; 950 | switch (*p) { 951 | case '\n': 952 | c=XK_Return; 953 | break; 954 | case '\t': 955 | c=XK_Tab; 956 | break; 957 | case '\033': 958 | c=XK_Escape; 959 | break; 960 | case '\b': 961 | c=XK_BackSpace; 962 | break; 963 | case '%': 964 | if (!escaped) { 965 | escaped=True; 966 | continue; 967 | } else { 968 | c=*p; 969 | break; 970 | } 971 | case '^': 972 | if (!escaped) { 973 | ev.xkey.state|=ControlMask; 974 | continue; 975 | } 976 | case '~': 977 | if (!escaped) { 978 | ev.xkey.state|=Mod1Mask; 979 | continue; 980 | } 981 | case '+': 982 | if (!escaped) { 983 | ev.xkey.state|=ShiftMask; 984 | continue; 985 | } 986 | case 'f': 987 | case 'F': { 988 | int f; 989 | if (escaped && p[1] && strchr("01", p[1]) && (p[2]>='0') && (p[2]<='9')) { 990 | char num[3]={0,0,0}; 991 | if (p[1]=='1') { 992 | strncpy(num,(char*)p+1,2); 993 | } else { 994 | num[0]=p[2]; 995 | } 996 | f=atoi(num); 997 | c= ((f>0)&&(f<13)) ? funkeys[f-1] : *p; 998 | p+=2; 999 | } else { 1000 | c=*p; 1001 | } 1002 | break; 1003 | } 1004 | case '.': 1005 | c=escaped?XK_Delete:*p; 1006 | break; 1007 | default: 1008 | c=*p; 1009 | } 1010 | n=strchr(numkeys_upper,c); 1011 | if (n) { 1012 | c=numkeys_lower[n-numkeys_upper]; 1013 | ev.xkey.state|=ShiftMask; 1014 | } else { 1015 | if (escaped && (c>='0') && (c<='9') && (c!='5')) { 1016 | c=navkeys[c-48]; 1017 | } else { 1018 | ev.xkey.state|=isupper(c)?ShiftMask:0; 1019 | } 1020 | } 1021 | ev.xkey.keycode=XKeysymToKeycode(disp,c); 1022 | ev.xkey.type=KeyPress; 1023 | XSendEvent(disp, win, True, KeyPressMask,&ev); 1024 | usleep(1000); 1025 | XSync(disp, False); 1026 | ev.xkey.time=CurrentTime; 1027 | ev.xkey.type=KeyRelease; 1028 | XSendEvent(disp, win, True, KeyPressMask,&ev); 1029 | usleep(1000); 1030 | XSync(disp, False); 1031 | ev.xkey.state=0; 1032 | escaped=False; 1033 | } 1034 | } 1035 | 1036 | /*********************************************************************/ 1037 | /* * * * * * * * * Clipboard and selection functions * * * * * * * * */ 1038 | /*********************************************************************/ 1039 | #include 1040 | /* xcout() contexts */ 1041 | #define XCLIB_XCOUT_NONE 0 /* no context */ 1042 | #define XCLIB_XCOUT_SENTCONVSEL 1 /* sent a request */ 1043 | #define XCLIB_XCOUT_INCR 2 /* in an incr loop */ 1044 | #define XCLIB_XCOUT_FALLBACK 3 /* UTF8_STRING failed, need fallback to XA_STRING */ 1045 | 1046 | /* xcin() contexts */ 1047 | #define XCLIB_XCIN_NONE 0 1048 | #define XCLIB_XCIN_INCR 2 1049 | 1050 | /* Retrieves the contents of a selections. Arguments are: 1051 | * 1052 | * A display that has been opened. 1053 | * A window 1054 | * An event to process 1055 | * The selection to return 1056 | * The target(UTF8_STRING or XA_STRING) to return 1057 | * A pointer to a char array to put the selection into. 1058 | * A pointer to a long to record the length of the char array 1059 | * A pointer to an int to record the context in which to process the event 1060 | * Returns ONE if retrieval of selection data is complete, or ZERO otherwise. 1061 | */ 1062 | static int xcout(Display*dpy, Window win, XEvent evt, Atom sel, Atom trg, uchar**txt, ulong*len, uint*ctx) 1063 | { 1064 | static Atom prop; /* for other windows to put their selection into */ 1065 | static Atom inc; 1066 | Atom prop_type; 1067 | Atom atomUTF8String; 1068 | int prop_fmt; 1069 | uchar *buffer; /* buffer for XGetWindowProperty to dump data into */ 1070 | ulong prop_size; 1071 | ulong prop_items; 1072 | uchar *ltxt = *txt; /* local buffer of text to return */ 1073 | 1074 | if (!prop) { prop = XInternAtom(dpy, "XCLIP_OUT", False); } 1075 | if (!inc) { inc = XInternAtom(dpy, "INCR", False); } 1076 | 1077 | switch (*ctx) { 1078 | case XCLIB_XCOUT_NONE: { /* there is no context, do an XConvertSelection() */ 1079 | if (*len > 0) { /* initialise return length to 0 */ 1080 | free(*txt); 1081 | *len = 0; 1082 | } 1083 | XConvertSelection(dpy, sel, trg, prop, win, CurrentTime); /* send selection request */ 1084 | *ctx = XCLIB_XCOUT_SENTCONVSEL; 1085 | return (0); 1086 | } 1087 | case XCLIB_XCOUT_SENTCONVSEL: { 1088 | atomUTF8String = XInternAtom(dpy, "UTF8_STRING", False); 1089 | if (evt.type != SelectionNotify) 1090 | return (0); 1091 | 1092 | /* fallback to XA_STRING when UTF8_STRING failed */ 1093 | if (trg == atomUTF8String && evt.xselection.property == None) { 1094 | *ctx = XCLIB_XCOUT_FALLBACK; 1095 | return (0); 1096 | } 1097 | 1098 | /* find the size and format of the data in property */ 1099 | XGetWindowProperty( dpy, win, prop, 0, 0, False, AnyPropertyType, 1100 | &prop_type, &prop_fmt, &prop_items, &prop_size, &buffer ); 1101 | XFree(buffer); 1102 | 1103 | if (prop_type == inc) { 1104 | XDeleteProperty(dpy, win, prop); /* start INCR mechanism by deleting property */ 1105 | XFlush(dpy); 1106 | *ctx = XCLIB_XCOUT_INCR; 1107 | return (0); 1108 | } 1109 | 1110 | /* if not incr, and not format == 8, it's nothing we understand anyway. */ 1111 | if (prop_fmt != 8) { 1112 | *ctx = XCLIB_XCOUT_NONE; 1113 | return (0); 1114 | } 1115 | 1116 | /* not using INCR mechanism, just read the property */ 1117 | XGetWindowProperty( dpy, win, prop, 0, (long) prop_size, False, AnyPropertyType, 1118 | &prop_type, &prop_fmt, &prop_items, &prop_size, &buffer ); 1119 | 1120 | /* finished with property, delete it */ 1121 | XDeleteProperty(dpy, win, prop); 1122 | 1123 | /* copy the buffer to the pointer for returned data */ 1124 | ltxt = (uchar*) malloc(prop_items); 1125 | memcpy(ltxt, buffer, prop_items); 1126 | 1127 | /* set the length of the returned data */ 1128 | *len = prop_items; 1129 | *txt = ltxt; 1130 | 1131 | /* free the buffer */ 1132 | XFree(buffer); 1133 | 1134 | *ctx = XCLIB_XCOUT_NONE; 1135 | 1136 | /* complete contents of selection fetched, return 1 */ 1137 | return (1); 1138 | } 1139 | case XCLIB_XCOUT_INCR: { 1140 | /* To use the INCR method, we delete the property with the selection in it, 1141 | wait for an event indicating that the property has been created, 1142 | then read it, delete it, etc. */ 1143 | 1144 | if (evt.type != PropertyNotify) { return (0); } 1145 | if (evt.xproperty.state != PropertyNewValue) { return (0); } 1146 | 1147 | /* check size and format of the property */ 1148 | XGetWindowProperty( dpy, win, prop, 0, 0, False, AnyPropertyType, 1149 | &prop_type, &prop_fmt, &prop_items, &prop_size, &buffer ); 1150 | 1151 | if (prop_fmt != 8) { 1152 | /* property is not text, deleting tells other client to send next property */ 1153 | XFree(buffer); 1154 | XDeleteProperty(dpy, win, prop); 1155 | return (0); 1156 | } 1157 | 1158 | if (prop_size == 0) { /* no more data, exit from loop */ 1159 | XFree(buffer); 1160 | XDeleteProperty(dpy, win, prop); 1161 | *ctx = XCLIB_XCOUT_NONE; 1162 | return (1); /* INCR transfer completed */ 1163 | } 1164 | XFree(buffer); 1165 | 1166 | /* if we have come this far, the propery contains text, and we know the size. */ 1167 | XGetWindowProperty( dpy, win, prop, 0, (long) prop_size, False, AnyPropertyType, 1168 | &prop_type, &prop_fmt, &prop_items, &prop_size, &buffer ); 1169 | /* allocate memory to accommodate data in *txt */ 1170 | if (*len == 0) { 1171 | *len = prop_items; 1172 | ltxt = (uchar*) malloc(*len); 1173 | } else { 1174 | *len += prop_items; 1175 | ltxt = (uchar*) realloc(ltxt, *len); 1176 | } 1177 | memcpy(<xt[*len - prop_items], buffer, prop_items); /* add data to ltxt */ 1178 | 1179 | *txt = ltxt; 1180 | XFree(buffer); 1181 | XDeleteProperty(dpy, win, prop); /* delete property to get the next item */ 1182 | XFlush(dpy); 1183 | return (0); 1184 | } 1185 | } 1186 | return (0); 1187 | } 1188 | 1189 | /* Put data into a selection, in response to a SelecionRequest event from 1190 | * another window (and any subsequent events relating to an INCR transfer). 1191 | * 1192 | * Arguments are: 1193 | * A display 1194 | * A window 1195 | * The event to respond to 1196 | * A pointer to an Atom. This gets set to the property nominated by the other 1197 | * app in it's SelectionRequest. Things are likely to break if you change the 1198 | * value of this yourself. 1199 | * The target (UTF8_STRING or XA_STRING) to respond to 1200 | * A pointer to an array of chars to read selection data from. 1201 | * The length of the array of chars. 1202 | * In case of an INCR transfer, the position within the array of chars that's being processed. 1203 | * The context that event is the be processed within. 1204 | */ 1205 | static int xcin( Display*dpy, Window*win, XEvent evt, Atom*prop, 1206 | Atom trg, uchar*txt, ulong len, ulong*pos, uint *ctx ) 1207 | { 1208 | ulong chunk_len; /* length of current chunk (for incr transfers only) */ 1209 | XEvent resp; /* response to event */ 1210 | static Atom inc; 1211 | static Atom targets; 1212 | static long chunk_size; 1213 | if (!targets) { targets = XInternAtom(dpy, "TARGETS", False); } 1214 | if (!inc) { inc = XInternAtom(dpy, "INCR", False); } 1215 | /* Treat selections larger than 1/4 of the max request size as "large" per ICCCM sect. 2.5 */ 1216 | if (!chunk_size) { 1217 | chunk_size = XExtendedMaxRequestSize(dpy) / 4; 1218 | if (!chunk_size) { chunk_size = XMaxRequestSize(dpy) / 4; } 1219 | } 1220 | switch (*ctx) { 1221 | case XCLIB_XCIN_NONE: { 1222 | if (evt.type != SelectionRequest) { return (0); } 1223 | *win = evt.xselectionrequest.requestor; 1224 | *prop = evt.xselectionrequest.property; 1225 | *pos = 0; 1226 | if (evt.xselectionrequest.target == targets) { /* put the data into an property */ 1227 | Atom types[2]; 1228 | int size=(int)(sizeof(types)/sizeof(Atom)); 1229 | types[0]=targets; 1230 | types[1]=trg; 1231 | /* send data all at once (not using INCR) */ 1232 | XChangeProperty(dpy,*win,*prop,XA_ATOM,32,PropModeReplace,(uchar*)types,size); 1233 | } else if (len > chunk_size) { 1234 | XChangeProperty(dpy,*win,*prop,inc,32,PropModeReplace,0,0); /* send INCR response */ 1235 | /* With INCR, we need to know when requestor window changes/deletes properties */ 1236 | XSelectInput(dpy, *win, PropertyChangeMask); 1237 | *ctx = XCLIB_XCIN_INCR; 1238 | } else { 1239 | XChangeProperty(dpy,*win,*prop,trg,8,PropModeReplace,(uchar*)txt,(int)len); /* All, not INCR */ 1240 | } 1241 | 1242 | /* FIXME? According to ICCCM section 2.5, we should confirm that X ChangeProperty 1243 | succeeded without any Alloc errors before replying with SelectionNotify. 1244 | However, doing so would require an error handler which modifies a global 1245 | variable, plus doing XSync after each X ChangeProperty. */ 1246 | resp.xselection.property = *prop; 1247 | resp.xselection.type = SelectionNotify; 1248 | resp.xselection.display = evt.xselectionrequest.display; 1249 | resp.xselection.requestor = *win; 1250 | resp.xselection.selection = evt.xselectionrequest.selection; 1251 | resp.xselection.target = evt.xselectionrequest.target; 1252 | resp.xselection.time = evt.xselectionrequest.time; 1253 | XSendEvent(dpy, evt.xselectionrequest.requestor, 0, 0, &resp); /* send response event */ 1254 | XFlush(dpy); 1255 | return (len > chunk_size) ? 0 : 1; /* if data sent all at once, transfer is complete. */ 1256 | break; 1257 | } 1258 | case XCLIB_XCIN_INCR: { 1259 | if (evt.type != PropertyNotify) { return (0); } /* ignore non-property events */ 1260 | if (evt.xproperty.state != PropertyDelete) { return (0); } /* only interest in deleted props */ 1261 | chunk_len = chunk_size; /* set length to max size */ 1262 | /* if a max-sized chunk length would extend past end, set length to remaining txt length */ 1263 | if ((*pos + chunk_len) > len) { chunk_len = len - *pos; } 1264 | 1265 | /* if start of chunk is beyond end of txt, then we've sent all data, so set length to zero */ 1266 | if (*pos > len) { chunk_len = 0; } 1267 | if (chunk_len) { /* put chunk into property */ 1268 | XChangeProperty(dpy,*win,*prop,trg,8,PropModeReplace,&txt[*pos],(int)chunk_len); 1269 | } else { 1270 | XChangeProperty(dpy,*win,*prop,trg,8,PropModeReplace,0,0); /* empty prop shows we're done */ 1271 | } 1272 | XFlush(dpy); 1273 | if (!chunk_len) { *ctx = XCLIB_XCIN_NONE; } /* all data is sent, break out of the loop */ 1274 | *pos += chunk_size; 1275 | return (chunk_len==0) ? 1 : 0; /* chunk_len == 0 means we finished the transfer. */ 1276 | break; 1277 | } 1278 | } 1279 | return (0); 1280 | } 1281 | 1282 | 1283 | 1284 | static Atom selarg_to_seltype(Display*dpy, char arg) 1285 | { 1286 | switch (arg) { 1287 | case 'p': return XA_PRIMARY; 1288 | case 's': return XA_SECONDARY; 1289 | case 'b': return XA_STRING; 1290 | case 'c': return XA_CLIPBOARD(dpy); 1291 | default:return XA_PRIMARY; 1292 | } 1293 | } 1294 | 1295 | 1296 | static Window make_selection_window(Display*dpy) 1297 | { 1298 | Window win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0, 0); 1299 | XSelectInput(dpy, win, PropertyChangeMask); 1300 | return win; 1301 | } 1302 | 1303 | 1304 | 1305 | XCTRL_API void set_selection(Display*dpy, char kind, char*sel_buf, Bool utf8) 1306 | { 1307 | Atom seltype=selarg_to_seltype(dpy,kind); 1308 | long sel_len=strlen(sel_buf); 1309 | if (seltype == XA_STRING) { 1310 | XStoreBuffer(dpy, (char*)sel_buf, (int)sel_len, 0); 1311 | } else { 1312 | XEvent evt; 1313 | Window win = make_selection_window(dpy); 1314 | Atom target = utf8 ? XA_UTF8_STRING(dpy) : XA_STRING; 1315 | /* FIXME: Should not use CurrentTime per ICCCM section 2.1 */ 1316 | XSetSelectionOwner(dpy, seltype, win, CurrentTime); 1317 | while (1) { /* wait for a SelectionRequest event */ 1318 | static uint clear = 0; 1319 | static uint context = XCLIB_XCIN_NONE; 1320 | static ulong sel_pos = 0; 1321 | static Window cwin; 1322 | static Atom pty; 1323 | int finished; 1324 | XNextEvent(dpy, &evt); 1325 | finished = xcin(dpy, &cwin, evt, &pty, target, (uchar*)sel_buf, sel_len, &sel_pos, &context); 1326 | if (evt.type == SelectionClear) { clear = 1; } 1327 | if ((context == XCLIB_XCIN_NONE) && clear) { break; } 1328 | if (finished) { break; } 1329 | } 1330 | XDestroyWindow(dpy,win); 1331 | } 1332 | } 1333 | 1334 | 1335 | 1336 | XCTRL_API uchar* get_selection(Display*dpy, char kind, Bool utf8) 1337 | { 1338 | uchar *sel_buf=NULL; /* buffer for selection data */ 1339 | ulong sel_len = 0; /* length of sel_buf */ 1340 | XEvent evt; /* X Event Structures */ 1341 | uchar*result=NULL; /* null-terminated copy of sel_buf */ 1342 | uint context = XCLIB_XCOUT_NONE; 1343 | Window win = make_selection_window(dpy); 1344 | Atom seltype = selarg_to_seltype(dpy,kind); 1345 | Atom target = utf8 ? XA_UTF8_STRING(dpy) : XA_STRING; 1346 | if (seltype == XA_STRING) { 1347 | sel_buf = (uchar*) XFetchBuffer(dpy, (int *) &sel_len, 0); 1348 | } else { 1349 | while (1) { 1350 | if (context != XCLIB_XCOUT_NONE) { XNextEvent(dpy, &evt); } 1351 | xcout(dpy, win, evt, seltype, target, &sel_buf, &sel_len, &context); 1352 | if (context == XCLIB_XCOUT_FALLBACK) { 1353 | context = XCLIB_XCOUT_NONE; 1354 | target = XA_STRING; 1355 | continue; 1356 | } 1357 | if (context == XCLIB_XCOUT_NONE) { break; } 1358 | } 1359 | } 1360 | if (sel_buf) { 1361 | result=(uchar*)calloc(sel_len+1,1); 1362 | strncpy((char*)result, (char*)sel_buf, sel_len); 1363 | result[sel_len]='\0'; 1364 | if (seltype == XA_STRING) { XFree(sel_buf); } else { free(sel_buf); } 1365 | } 1366 | XDestroyWindow(dpy,win); 1367 | return result; 1368 | } 1369 | 1370 | 1371 | /*********************************************************************/ 1372 | /* * * * * * * * * * * * * Event Listener * * * * * * * * * * * * * */ 1373 | /*********************************************************************/ 1374 | 1375 | /* 1376 | Portions of the event listener were adapted from "F***ing Small Panel" 1377 | Copyright (c) 2000-2002 by Peter Zelezny, which was released under a 1378 | MIT/X style license. 1379 | */ 1380 | 1381 | static Window*get_net_client_list(Display*disp, ulong*nitems) { 1382 | Atom type=XA_WINDOW; 1383 | static Atom a=0; 1384 | static Display*old_disp=NULL; 1385 | Atom ret_type; 1386 | int format; 1387 | ulong after=0; 1388 | unsigned char *retp; 1389 | if (disp!=old_disp) { 1390 | old_disp=disp; 1391 | a=XInternAtom(disp, "_NET_CLIENT_LIST", False); 1392 | } 1393 | if (XGetWindowProperty(disp,DefRootWin,a,0,1024,False,type,&ret_type,&format,nitems,&after,&retp) != Success) { 1394 | *nitems=0; 1395 | return NULL; 1396 | } 1397 | if (ret_type != type) { 1398 | XFree(retp); 1399 | *nitems=0; 1400 | return NULL; 1401 | } 1402 | return (Window*)retp; 1403 | } 1404 | 1405 | 1406 | 1407 | static int window_has_prop(Display*disp, Window w, Atom a, Atom type) { 1408 | Atom ret_type; 1409 | int format; 1410 | ulong nitems; 1411 | ulong after; 1412 | unsigned char *retp; 1413 | int ret=0; 1414 | if (XGetWindowProperty(disp,w,a,0,1024,False,type,&ret_type,&format,&nitems,&after,&retp) == Success) { 1415 | if (retp) { 1416 | XFree(retp); 1417 | if (ret_type == type) { ret=1; } 1418 | } 1419 | } 1420 | return ret; 1421 | } 1422 | 1423 | 1424 | 1425 | static int has_net_wm_name(Display*disp, Window w) { 1426 | static Display*old_disp=NULL; 1427 | static Atom type=0; 1428 | static Atom a=0; 1429 | if (disp!=old_disp) { 1430 | old_disp=disp; 1431 | a=XInternAtom(disp, "_NET_WM_NAME", False); 1432 | type=XInternAtom(disp, "UTF8_STRING", False); 1433 | } 1434 | return window_has_prop(disp, w, a, type); 1435 | } 1436 | 1437 | 1438 | 1439 | static int has_net_wm_state(Display*disp, Window w) { 1440 | static Display*old_disp=NULL; 1441 | static Atom a=0; 1442 | if (disp!=old_disp) { 1443 | old_disp=disp; 1444 | a=XInternAtom(disp, "_NET_WM_STATE", False); 1445 | } 1446 | return window_has_prop(disp, w, a, XA_ATOM); 1447 | } 1448 | 1449 | 1450 | 1451 | typedef struct _WinListItem { 1452 | struct _WinListItem*next; 1453 | Window win; 1454 | } WinListItem; 1455 | 1456 | 1457 | 1458 | static void winlist_add_item(WinListItem**list, Display*dpy, Window win) 1459 | { 1460 | WinListItem*t=calloc(1,sizeof(WinListItem)); 1461 | t->win=win; 1462 | XSelectInput(dpy,win,PropertyChangeMask|FocusChangeMask|StructureNotifyMask); 1463 | if (!*list) { 1464 | *list=t; 1465 | return; 1466 | } else { 1467 | WinListItem*p=*list; 1468 | while (p->next) { p=p->next; } 1469 | p->next=t; 1470 | } 1471 | } 1472 | 1473 | 1474 | 1475 | static void winlist_del_item(WinListItem**list, Window win) 1476 | { 1477 | WinListItem*cur; 1478 | WinListItem*prv=NULL; 1479 | for (cur=*list; cur; cur=cur->next) { 1480 | if (cur->win==win) { 1481 | if (prv) { 1482 | prv->next=cur->next; 1483 | } else { 1484 | *list=cur->next; 1485 | } 1486 | free(cur); 1487 | return; 1488 | } 1489 | prv=cur; 1490 | } 1491 | } 1492 | 1493 | 1494 | 1495 | static void winlist_free_all(WinListItem*list) 1496 | { 1497 | WinListItem*p=list; 1498 | while (p) { 1499 | WinListItem*n=p->next; 1500 | free(p); 1501 | p=n; 1502 | } 1503 | } 1504 | 1505 | 1506 | /* Set this to 1 to print unhandled events to stderr */ 1507 | #define PRINT_UNHANDLED_EVENTS 0 1508 | 1509 | #define EVENT_ATOM_COUNT (sizeof(event_names)/sizeof(char*)) 1510 | 1511 | 1512 | XCTRL_API void event_loop(Display*disp, EventCallback cb, void*cb_data) 1513 | { 1514 | XEvent ev; 1515 | enum { 1516 | EV_NET_ACTIVE_WINDOW, 1517 | EV_NET_CLIENT_LIST, 1518 | EV_NET_CURRENT_DESKTOP, 1519 | EV_NET_WM_NAME, 1520 | EV_NET_WM_ICON_NAME, 1521 | EV_NET_WM_STATE, 1522 | EV_WM_NAME, 1523 | EV_WM_ICON_NAME, 1524 | EV_WM_STATE 1525 | }; 1526 | static char*event_names[]={ 1527 | "_NET_ACTIVE_WINDOW", 1528 | "_NET_CLIENT_LIST", 1529 | "_NET_CURRENT_DESKTOP", 1530 | "_NET_WM_NAME", 1531 | "_NET_WM_ICON_NAME", 1532 | "_NET_WM_STATE", 1533 | "WM_NAME", 1534 | "WM_ICON_NAME", 1535 | "WM_STATE" 1536 | }; 1537 | static Display*old_disp=NULL; 1538 | static Atom event_atoms[EVENT_ATOM_COUNT]={0,}; 1539 | WinListItem*ev_winlist=NULL; 1540 | ulong n=0; 1541 | ulong i; 1542 | Window*clients=get_net_client_list(disp, &n); 1543 | for (i=0; inext; 1571 | for (i=0; iwin == clients[i]) { 1573 | do_del=0; 1574 | break; 1575 | } 1576 | } 1577 | if ( do_del ) { 1578 | Window x=p1->win; 1579 | winlist_del_item(&ev_winlist, p1->win); 1580 | rv=cb(XCTRL_EVENT_WINDOW_LIST_DELETE,x,cb_data); 1581 | } 1582 | p1=p2; 1583 | } 1584 | for (i = 0; i < n; i++) { /* Add any new windows */ 1585 | int found=0; 1586 | for (p1=ev_winlist;p1;p1=p1->next) { 1587 | if (clients[i]==p1->win) { 1588 | found=1; 1589 | break; 1590 | } 1591 | } 1592 | if (!found) { 1593 | winlist_add_item(&ev_winlist,disp,clients[i]); 1594 | rv=cb(XCTRL_EVENT_WINDOW_LIST_INSERT,clients[i],cb_data); 1595 | } 1596 | } 1597 | if (clients) { XFree(clients); } 1598 | break; 1599 | } 1600 | case EV_NET_CURRENT_DESKTOP: { 1601 | rv=cb(XCTRL_EVENT_DESKTOP_SWITCH,get_current_desktop(disp),cb_data); 1602 | break; 1603 | } 1604 | case EV_NET_WM_NAME: { 1605 | rv=cb(XCTRL_EVENT_WINDOW_TITLE,ev.xproperty.window,cb_data); 1606 | break; 1607 | } 1608 | case EV_NET_WM_STATE: { 1609 | rv=cb(XCTRL_EVENT_WINDOW_STATE,ev.xproperty.window,cb_data); 1610 | break; 1611 | } 1612 | case EV_WM_NAME: { /* ignore WM_NAME if we can use _NET_WM_NAME instead */ 1613 | if (!has_net_wm_name(disp,ev.xproperty.window)) { 1614 | rv=cb(XCTRL_EVENT_WINDOW_TITLE,ev.xproperty.window,cb_data); 1615 | } 1616 | break; 1617 | } 1618 | case EV_WM_STATE: { /* ignore WM_STATE if we can use _NET_WM_STATE instead */ 1619 | if (!has_net_wm_state(disp,ev.xproperty.window)) { 1620 | rv=cb(XCTRL_EVENT_WINDOW_STATE,ev.xproperty.window,cb_data); 1621 | } 1622 | break; 1623 | } 1624 | case EV_NET_WM_ICON_NAME: { break; } /* unused */ 1625 | case EV_WM_ICON_NAME: { break; } /* unused */ 1626 | default: { 1627 | # if PRINT_UNHANDLED_EVENTS 1628 | char*nm=XGetAtomName(disp, ev.xproperty.atom); 1629 | fprintf(stderr, "PropertyNotify: unhandled atom \"%s\" for window %ld\n", nm, ev.xproperty.window); 1630 | XFree(nm); 1631 | # endif 1632 | } 1633 | } 1634 | break; 1635 | } 1636 | case ConfigureNotify: { 1637 | rv=cb(XCTRL_EVENT_WINDOW_MOVE_RESIZE,ev.xconfigure.window,cb_data); 1638 | break; 1639 | } 1640 | case FocusIn: { 1641 | rv=cb(XCTRL_EVENT_WINDOW_FOCUS_GAINED,ev.xfocus.window,cb_data); 1642 | break; 1643 | } 1644 | case FocusOut: { 1645 | rv=cb(XCTRL_EVENT_WINDOW_FOCUS_LOST,ev.xfocus.window,cb_data); 1646 | break; 1647 | } 1648 | case DestroyNotify: { break; } /* unused */ 1649 | case UnmapNotify: { break; } /* unused */ 1650 | case MapNotify: { break; } /* unused */ 1651 | default: { 1652 | # if PRINT_UNHANDLED_EVENTS 1653 | fprintf(stderr, "Unhandled event of type %d\n", ev.type); 1654 | # endif 1655 | } 1656 | } 1657 | if (!rv) { break; } 1658 | } 1659 | winlist_free_all(ev_winlist); 1660 | } 1661 | 1662 | -------------------------------------------------------------------------------- /src/xctrl.h: -------------------------------------------------------------------------------- 1 | /* 2 | X11 control library, based largely on Tomas Styblo's "wmctrl". 3 | 4 | The original wmctrl is Copyright (C) 2003 Tomas Styblo 5 | and is available from http://tripie.sweb.cz/utils/wmctrl/ 6 | 7 | This library derived in 2010 by Jeffrey Pohlmeyer 8 | 9 | The charset conversion routines were derived from Text-Unaccent-1.08/unac.c 10 | Copyright (C) 2000, 2001, 2002, 2003 Loic Dachary 11 | Text-Unaccent is available from http://search.cpan.org/~ldachary/ 12 | 13 | The clipboard and selection routines were derived from xclip, 14 | a command line interface to X server selections. 15 | Copyright (C) 2001 Kim Saunders 16 | Copyright (C) 2007-2008 Peter Astrand 17 | xclip is available from http://sourceforge.net/projects/xclip/ 18 | 19 | All source code mentioned above is released under the following terms: 20 | 21 | This program is free software, released under the GNU General Public 22 | License. You may redistribute and/or modify this program under the terms 23 | of that license as published by the Free Software Foundation; either 24 | version 2 of the License, or (at your option) any later version. 25 | 26 | This program is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | GNU General Public License for more details. 30 | 31 | To get a copy of the GNU General Puplic License, write to the 32 | Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 33 | 34 | */ 35 | 36 | 37 | 38 | #ifndef XCTRL_API 39 | # define XCTRL_API 40 | #endif 41 | 42 | typedef unsigned long ulong; 43 | typedef unsigned char uchar; 44 | typedef unsigned int uint; 45 | 46 | 47 | #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ 48 | #define _NET_WM_STATE_ADD 1 /* add/set property */ 49 | #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ 50 | 51 | 52 | #define XCTRL_GEOM_USE_X (1 << 8) 53 | #define XCTRL_GEOM_USE_Y (1 << 9) 54 | #define XCTRL_GEOM_USE_W (1 << 10) 55 | #define XCTRL_GEOM_USE_H (1 << 11) 56 | 57 | 58 | /* Definitions for Motif-style WM Hints. (Courtesy of FOX-toolkit FXWindow.cpp) */ 59 | #define XCTRL_MWM_HINTS_FUNCTIONS (1L << 0) /* Definitions for MotifHints.flags */ 60 | #define XCTRL_MWM_HINTS_DECORATIONS (1L << 1) 61 | #define XCTRL_MWM_HINTS_INPUT_MODE (1L << 2) 62 | #define XCTRL_MWM_HINTS_ALL (MWM_HINTS_FUNCTIONS|MWM_HINTS_DECORATIONS|MWM_HINTS_INPUT_MODE) 63 | 64 | #define XCTRL_MWM_DECOR_NONE (0) 65 | #define XCTRL_MWM_DECOR_ALL (1L << 0) /* Definitions for MotifHints.decorations */ 66 | #define XCTRL_MWM_DECOR_BORDER (1L << 1) 67 | #define XCTRL_MWM_DECOR_RESIZEH (1L << 2) 68 | #define XCTRL_MWM_DECOR_TITLE (1L << 3) 69 | #define XCTRL_MWM_DECOR_MENU (1L << 4) 70 | #define XCTRL_MWM_DECOR_MINIMIZE (1L << 5) 71 | #define XCTRL_MWM_DECOR_MAXIMIZE (1L << 6) 72 | 73 | #define XCTRL_MWM_FUNC_NONE (0) 74 | #define XCTRL_MWM_FUNC_ALL (1L << 0) /* Definitions for MotifHints.functions */ 75 | #define XCTRL_MWM_FUNC_RESIZE (1L << 1) 76 | #define XCTRL_MWM_FUNC_MOVE (1L << 2) 77 | #define XCTRL_MWM_FUNC_MINIMIZE (1L << 3) 78 | #define XCTRL_MWM_FUNC_MAXIMIZE (1L << 4) 79 | #define XCTRL_MWM_FUNC_CLOSE (1L << 5) 80 | 81 | #define XCTRL_MWM_INPUT_MODELESS 0 /* Values for MotifHints.inputmode */ 82 | #define XCTRL_MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 83 | #define XCTRL_MWM_INPUT_SYSTEM_MODAL 2 84 | #define XCTRL_MWM_INPUT_FULL_APPLICATION_MODAL 3 85 | 86 | 87 | typedef struct _Geometry { 88 | int x; 89 | int y; 90 | unsigned int w; 91 | unsigned int h; 92 | } Geometry; 93 | 94 | /* 95 | Any functions that return a pointer should 96 | be disposed of by the caller with free() 97 | */ 98 | 99 | /* Charset functions */ 100 | XCTRL_API void init_charset(Bool force_utf8, char*charset); 101 | XCTRL_API char* convert_locale(const char*src, const char*from, const char*to); 102 | XCTRL_API char* locale_to_utf8(const char*src); 103 | XCTRL_API char* utf8_to_locale(const char*src); 104 | 105 | /* Window selection functions */ 106 | XCTRL_API Window* get_window_list(Display*disp, ulong*size); 107 | XCTRL_API Window select_window(Display*disp, int button); /* -1 = any button */ 108 | XCTRL_API Window get_active_window(Display*disp); 109 | 110 | /* Window information and manipulation functions */ 111 | XCTRL_API Bool activate_window(Display*disp, Window win, Bool switch_desk); 112 | XCTRL_API int set_window_state(Display*disp, Window win, ulong action, const char*p1, const char*p2); 113 | XCTRL_API Bool iconify_window(Display*disp, Window win); 114 | XCTRL_API int close_window(Display*disp, Window win); 115 | 116 | XCTRL_API char* get_window_class(Display*disp, Window win); 117 | XCTRL_API char* get_window_title(Display*disp, Window win); 118 | XCTRL_API void set_window_title(Display*disp, Window win, const char*title, char mode); 119 | 120 | XCTRL_API int set_window_geom(Display*disp, Window win, long grav, long flags, long x, long y, long w, long h); 121 | XCTRL_API void get_window_geom(Display*disp, Window win, Geometry*geom); 122 | XCTRL_API Bool get_window_frame(Display*disp, Window win, long*left, long*right, long*top, long*bottom); 123 | XCTRL_API char*get_window_type(Display*disp, Window win); 124 | XCTRL_API void set_window_mwm_hints(Display*disp, Window win, ulong flags, ulong funcs, ulong decors, ulong imode); 125 | 126 | XCTRL_API int send_window_to_desktop(Display*disp, Window win, int desktop); 127 | XCTRL_API long get_desktop_of_window(Display*disp, Window win); 128 | 129 | XCTRL_API ulong get_win_pid(Display*disp, Window win); 130 | XCTRL_API char* get_client_machine(Display*disp, Window win); 131 | XCTRL_API void send_keystrokes(Display*disp, Window win, const char*keys); 132 | 133 | /* Desktop information and manipulation functions */ 134 | XCTRL_API int get_showing_desktop(Display*disp); 135 | XCTRL_API int set_showing_desktop(Display*disp, ulong state); 136 | 137 | XCTRL_API char* get_desktop_name(Display*disp, int desknum, Bool force_utf8); 138 | XCTRL_API int get_workarea_geom(Display*disp, Geometry*geom, int desknum); 139 | 140 | XCTRL_API int get_desktop_geom(Display*disp, int desknum, Geometry*geom); 141 | XCTRL_API int change_geometry(Display*disp, ulong x, ulong y); 142 | XCTRL_API int change_viewport(Display*disp, ulong x, ulong y); 143 | 144 | XCTRL_API long get_number_of_desktops(Display*disp); 145 | XCTRL_API int set_number_of_desktops(Display*disp, ulong n); 146 | 147 | XCTRL_API long get_current_desktop(Display*disp); 148 | XCTRL_API int set_current_desktop(Display*disp, ulong target); 149 | 150 | 151 | /* Window manager information functions */ 152 | XCTRL_API Window supporting_wm_check(Display*disp); 153 | XCTRL_API char* get_wm_name(Display*disp); 154 | XCTRL_API char* get_wm_class(Display*disp); 155 | XCTRL_API ulong get_wm_pid(Display*disp); 156 | XCTRL_API Bool wm_supports(Display*disp, const char*prop); 157 | 158 | XCTRL_API void set_selection(Display*dpy, char kind, char*sel_buf, Bool utf8); 159 | XCTRL_API uchar* get_selection(Display* dpy, char kind, Bool utf8); 160 | 161 | 162 | /* Event listener event types */ 163 | enum { 164 | XCTRL_EVENT_WINDOW_LIST_INSERT, 165 | XCTRL_EVENT_WINDOW_LIST_DELETE, 166 | XCTRL_EVENT_WINDOW_FOCUS_GAINED, 167 | XCTRL_EVENT_WINDOW_FOCUS_LOST, 168 | XCTRL_EVENT_WINDOW_MOVE_RESIZE, 169 | XCTRL_EVENT_WINDOW_TITLE, 170 | XCTRL_EVENT_WINDOW_STATE, 171 | XCTRL_EVENT_DESKTOP_SWITCH 172 | }; 173 | 174 | /* Event listener callback type */ 175 | typedef int (*EventCallback) (int ev, Window win, void*cb_data); 176 | 177 | /* Event listener function */ 178 | XCTRL_API void event_loop(Display*disp, EventCallback cb, void*cb_data); 179 | 180 | 181 | --------------------------------------------------------------------------------