├── .github └── workflows │ └── ci.yml ├── .gitignore ├── COPYING ├── Makefile ├── README.md ├── actions.c ├── actions.h ├── bar.c ├── bar.h ├── communications.c ├── communications.h ├── completions.c ├── completions.h ├── config.h ├── data.h ├── editor.c ├── editor.h ├── events.c ├── events.h ├── format.c ├── format.h ├── frame.c ├── frame.h ├── globals.c ├── globals.h ├── history.c ├── history.h ├── hook.c ├── hook.h ├── input.c ├── input.h ├── linkedlist.c ├── linkedlist.h ├── manage.c ├── manage.h ├── messages.h ├── number.c ├── number.h ├── sbuf.c ├── sbuf.h ├── screen.c ├── screen.h ├── sdorfehs.1 ├── sdorfehs.c ├── sdorfehs.h ├── split.c ├── split.h ├── utf8.c ├── utf8.h ├── util.c ├── util.h ├── vscreen.c ├── vscreen.h ├── window.c ├── window.h ├── xrandr.c └── xrandr.h /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | linux-build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: install dependencies 15 | run: sudo apt-get install -y libx11-dev libxft-dev libxrandr-dev libxtst-dev libxres-dev 16 | - name: install clang-tools for make regress 17 | run: sudo apt-get install -y clang-tools 18 | - name: make 19 | run: make 20 | - name: scan-build 21 | run: make regress 22 | - name: test install with DESTDIR 23 | run: sudo make install DESTDIR=/usr/gooses; size /usr/gooses/usr/local/bin/sdorfehs 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | sdorfehs 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION= 1.6 2 | 3 | VERSION!= [ -d .git ] && \ 4 | echo "git-`git rev-list --abbrev-commit --tags --max-count=1`" || \ 5 | echo "${VERSION}" 6 | 7 | CC?= cc 8 | PREFIX?= /usr/local 9 | PKGLIBS= x11 xft xrandr xtst xres freetype2 10 | CFLAGS+= -O2 -Wall \ 11 | -Wunused -Wmissing-prototypes -Wstrict-prototypes \ 12 | `pkg-config --cflags ${PKGLIBS}` \ 13 | -DVERSION=\"${VERSION}\" 14 | LDFLAGS+= `pkg-config --libs ${PKGLIBS}` 15 | 16 | # uncomment to enable debugging 17 | #CFLAGS+= -g -DDEBUG=1 18 | # and this for subsystem-specific debugging 19 | #CFLAGS+= -DINPUT_DEBUG=1 20 | #CFLAGS+= -DSENDCMD_DEBUG=1 21 | 22 | BINDIR= ${DESTDIR}$(PREFIX)/bin 23 | MANDIR?= ${DESTDIR}$(PREFIX)/man/man1 24 | 25 | SRC!= ls *.c 26 | OBJ= ${SRC:.c=.o} 27 | 28 | BIN= sdorfehs 29 | MAN= sdorfehs.1 30 | 31 | all: sdorfehs 32 | 33 | sdorfehs: $(OBJ) 34 | $(CC) -o $@ $(OBJ) $(LDFLAGS) 35 | 36 | install: all 37 | mkdir -p $(BINDIR) $(MANDIR) 38 | install -s $(BIN) $(BINDIR) 39 | install -m 644 $(MAN) $(MANDIR) 40 | 41 | regress: 42 | scan-build $(MAKE) 43 | 44 | clean: 45 | rm -f $(BIN) $(OBJ) 46 | 47 | .PHONY: all install clean 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## sdorfehs 2 | 3 | 4 | #### (pronounced "starfish") 5 | 6 | sdorfehs is a tiling window manager descended from 7 | [ratpoison](https://www.nongnu.org/ratpoison/) 8 | (which itself is modeled after 9 | [GNU Screen](https://www.gnu.org/software/screen/)). 10 | 11 | sdorfehs divides the screen into one or more frames, each only displaying 12 | one window at a time but can cycle through all available windows (those 13 | which are not being shown in another frame). 14 | 15 | Like Screen, sdorfehs primarily uses prefixed/modal key bindings for most 16 | actions. 17 | sdorfehs's command mode is entered with a configurable keystroke 18 | (`Control+a` by default) which then allows a number of bindings accessible 19 | with just a single keystroke or any other combination. 20 | For example, to cycle through available windows in a frame, press 21 | `Control+a` then `n`. 22 | 23 | ### License 24 | 25 | sdorfehs retains ratpoison's GPL2 license. 26 | 27 | ### Compiling 28 | 29 | Run `make` to compile, and `make install` to install to `/usr/local` by 30 | default. 31 | 32 | ### Wiki 33 | 34 | The 35 | [sdorfehs Wiki](https://github.com/jcs/sdorfehs/wiki) 36 | has tips and tricks, and information on troubleshooting problems. 37 | 38 | ## Features 39 | 40 | sdorfehs retains most of ratpoison's features while adding some more modern 41 | touches. 42 | 43 | ![Screenshot](https://jcs.org/images/sdorfehs-20190826.png) 44 | 45 | ### EWMH 46 | 47 | sdorfehs strives to be a bit more 48 | [EWMH](https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html) 49 | compliant, supporting Atoms such as `_NET_ACTIVE_WINDOW` for compositors 50 | to dim unfocused windows, and `_NET_WM_STATE_FULLSCREEN` to allow full-screen 51 | programs like VLC and Firefox to take over the entire screen when requested 52 | (though still allowing the standard key bindings to switch windows or kill 53 | the program). 54 | 55 | ### Virtual Screens 56 | 57 | sdorfehs adds virtual screens which each have their own frame configuration 58 | and set of windows. 59 | Windows can be moved between virtual screens with `vmove`. 60 | By default, virtual screens can be selected (via the `vselect` command) 61 | with `Control+a, F1` through `Control+a, F12`. 62 | 63 | ### Bar 64 | 65 | sdorfehs has a bar window which displays status messages or the output of 66 | certain commands. 67 | By default, this bar is made sticky with the `barsticky` setting which 68 | forces the bar to be permanently affixed to the top or bottom (configurable 69 | with the `bargravity` setting) of every virtual screen displaying the 70 | currently-focused window's title on the left side. 71 | 72 | When the bar is sticky, it also enables a mechanism to display arbitrary 73 | text on the right side, similar to bar programs for other window managers like 74 | i3bar, dzen2, etc. 75 | sdorfehs creates a 76 | [named pipe](https://en.wikipedia.org/wiki/Named_pipe) 77 | at `~/.config/sdorfehs/bar` and any text input into the pipe shows up on 78 | the bar, making it easy to integrate with standard utilities which can just 79 | echo into the pipe. 80 | For an extremely simple example, a shell script can just echo the output of 81 | `date` into the pipe once a second. 82 | 83 | while true; do 84 | date > ~/.config/sdorfehs/bar 85 | sleep 1 86 | done 87 | 88 | Bar input supports some markup commands from dzen2 in the format 89 | `^command(details)` which affect the text following the command until the 90 | command is reset with `^command()`. 91 | Currently supported commands: 92 | 93 | - `^ca(btn,cmd,btn2,cmd2)`: execute `cmd` when mouse button `btn` is clicked on 94 | this area of text, or `cmd2` if button `btn2` is clicked. 95 | Closing the area of clickable text can be done with `^ca()`. 96 | 97 | - `^fg(color)`: color the text following until the next `^fg()` command. 98 | A line of text such as `hello ^fg(green)world^fg()!` will color `hello` with 99 | the default foreground color (`set fgcolor`), then `world` in green, and the 100 | exclamation point with the default color. 101 | Colors can be specified as their common name (`blue`) or as a hex code 102 | (`#0000ff`). 103 | 104 | - `^fn(font)`: change the font of the following text. 105 | Fonts must be specified in Xft format like `set font` such as 106 | `^fn(noto emoji:size=13)`. 107 | Resetting to the default font can be done with `^fn()`. 108 | 109 | ### Gaps 110 | 111 | sdorfehs enables a configurable gap (with `set gap`) between frames by 112 | default to look a bit nicer on larger displays. 113 | 114 | ### Secure Remote Control 115 | 116 | sdorfehs's `-c` command line option uses a more secure IPC mechanism 117 | than Ratpoison for sending commands to a running sdorfehs process, 118 | such as a script controlling sdorfehs to restore a particular layout. 119 | 120 | Ratpoison's IPC only requires that a process create a new X11 window 121 | and set an Atom on it, which the parent Ratpoison process reads and 122 | executes the value of that Atom. 123 | An unprivileged application that only has an X11 connection (say a 124 | sandboxed Firefox child process) could set the `RP_COMMAND_REQUEST` 125 | Atom property on its existing window with a value of `0exec 126 | something` to cause Ratpoison to read it and execute that shell 127 | command as the user id running Ratpoison. 128 | 129 | sdorfehs's IPC mechanism switches to a Unix socket in the 130 | `~/.config/sdorfehs` directory which ensures the requesting process 131 | has the ability to make socket connections and can write to that 132 | path. 133 | -------------------------------------------------------------------------------- /actions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Prototypes of all actions that can be performed with keystrokes. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_ACTIONS_H 21 | #define _SDORFEHS_ACTIONS_H 1 22 | 23 | #include "sdorfehs.h" 24 | 25 | /* The structure returned by a command. */ 26 | typedef struct cmdret { 27 | char *output; 28 | int success; 29 | } cmdret; 30 | 31 | void clear_frame_undos(void); 32 | cmdret *frestore(char *data, rp_vscreen *v); 33 | char *fdump(rp_vscreen *vscreen); 34 | rp_keymap *find_keymap(char *name); 35 | void init_user_commands(void); 36 | void initialize_default_keybindings(void); 37 | cmdret *command(int interactive, char *data); 38 | cmdret *cmdret_new(int success, char *fmt,...); 39 | void cmdret_free(cmdret *ret); 40 | void free_user_commands(void); 41 | void free_aliases(void); 42 | void free_keymaps(void); 43 | char *wingravity_to_string(int g); 44 | rp_action *find_keybinding(KeySym keysym, unsigned int state, rp_keymap *map); 45 | rp_action *find_keybinding_by_action(char *action, rp_keymap *map); 46 | int spawn(char *cmd, rp_frame *frame); 47 | int vspawn(char *cmd, rp_frame *frame, rp_vscreen *vscreen); 48 | 49 | #endif /* ! _SDORFEHS_ACTIONS_H */ 50 | -------------------------------------------------------------------------------- /bar.h: -------------------------------------------------------------------------------- 1 | /* 2 | * functions for managing the program bar 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_BAR_H 21 | #define _SDORFEHS_BAR_H 1 22 | 23 | int bar_mkfifo(void); 24 | void init_bar(void); 25 | void bar_reset_alarm(void); 26 | void redraw_sticky_bar_text(int force); 27 | void update_window_names(rp_screen *s, char *fmt); 28 | void update_vscreen_names(rp_screen *s); 29 | void update_bar(rp_screen *s); 30 | void show_bar(rp_screen *s, char *fmt); 31 | void show_vscreen_bar(rp_screen *s); 32 | void hide_bar(rp_screen *s, int force); 33 | int bar_y(rp_screen *s, int height); 34 | int bar_x(rp_screen *s, int width); 35 | int sticky_bar_height(rp_screen *s); 36 | 37 | void message(char *s); 38 | void marked_message(char *s, int mark_start, int mark_end, int bar_type); 39 | void marked_message_printf(int mark_start, int mark_end, char *fmt,...); 40 | void redraw_last_message(void); 41 | void show_last_message(void); 42 | void free_bar(void); 43 | 44 | void bar_handle_click(rp_screen *s, XButtonEvent *e); 45 | 46 | int bar_open_fifo(void); 47 | void bar_read_fifo(void); 48 | 49 | #endif /* ! _SDORFEHS_BAR_H */ 50 | -------------------------------------------------------------------------------- /communications.c: -------------------------------------------------------------------------------- 1 | /* 2 | * communications.c -- Send commands to a running copy of sdorfehs. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "sdorfehs.h" 36 | 37 | #define BUFSZ 1024 38 | 39 | void 40 | init_control_socket_path(void) 41 | { 42 | char *config_dir; 43 | 44 | config_dir = get_config_dir(); 45 | rp_glob_screen.control_socket_path = xsprintf("%s/control", config_dir); 46 | free(config_dir); 47 | } 48 | 49 | void 50 | listen_for_commands(void) 51 | { 52 | struct sockaddr_un sun; 53 | 54 | if ((rp_glob_screen.control_socket_fd = socket(AF_UNIX, 55 | SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) == -1) 56 | err(1, "socket"); 57 | 58 | if (strlen(rp_glob_screen.control_socket_path) >= sizeof(sun.sun_path)) 59 | err(1, "control socket path too long: %s", 60 | rp_glob_screen.control_socket_path); 61 | 62 | strncpy(sun.sun_path, rp_glob_screen.control_socket_path, 63 | sizeof(sun.sun_path)-1); 64 | sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; 65 | sun.sun_family = AF_UNIX; 66 | 67 | if (unlink(rp_glob_screen.control_socket_path) == -1 && 68 | errno != ENOENT) 69 | err(1, "unlink %s",rp_glob_screen.control_socket_path); 70 | 71 | if (bind(rp_glob_screen.control_socket_fd, (struct sockaddr *)&sun, 72 | sizeof(sun)) == -1) 73 | err(1, "bind %s", rp_glob_screen.control_socket_path); 74 | 75 | if (chmod(rp_glob_screen.control_socket_path, 0600) == -1) 76 | err(1, "chmod %s", rp_glob_screen.control_socket_path); 77 | 78 | if (listen(rp_glob_screen.control_socket_fd, 2) == -1) 79 | err(1, "listen %s", rp_glob_screen.control_socket_path); 80 | 81 | PRINT_DEBUG(("listening for commands at %s\n", 82 | rp_glob_screen.control_socket_path)); 83 | } 84 | 85 | static ssize_t 86 | recv_unix(int fd, char **callerbuf) 87 | { 88 | int firstloop; 89 | char *message; 90 | ssize_t len, count; 91 | 92 | int flags = 0x0; 93 | int retries = 0; 94 | 95 | #ifdef SENDCMD_DEBUG 96 | pid_t pid = getpid(); 97 | char *dpfx = xsprintf("recv_unix_%d", pid); 98 | #endif 99 | WARNX_DEBUG("%s: enter\n", dpfx); 100 | 101 | message = xmalloc(BUFSZ); 102 | memset(message, 0, BUFSZ); 103 | 104 | len = 0; 105 | firstloop = 1; 106 | 107 | while ((count = recv(fd, message + len, BUFSZ, flags))) { 108 | if (count == -1) { 109 | switch (errno) { 110 | /* 111 | * message is complete 112 | */ 113 | case ECONNRESET: /* sender finished and closed */ 114 | case EAGAIN: /* no more left to read */ 115 | WARNX_DEBUG("%s: done: e%d\n", dpfx, errno); 116 | break; 117 | /* 118 | * transient conditions for retrying 119 | */ 120 | case ECONNREFUSED: 121 | case ENOMEM: 122 | if (retries++ >= 5) { 123 | warn("recv_unix: retries exhausted"); 124 | len = -1; 125 | break; 126 | } 127 | usleep(200); 128 | /* fallthrough */ 129 | case EINTR: 130 | warn("recv_unix: trying again"); 131 | continue; 132 | /* 133 | * usage error or untenable situation: 134 | * EBADF, EFAULT, EINVAL, ENOTCONN, ENOTSOCK 135 | */ 136 | default: 137 | warn("unanticipated receive error"); 138 | len = -1; 139 | } 140 | break; 141 | } 142 | if (firstloop) { 143 | WARNX_DEBUG("%s: first recv: %zd\n", dpfx, count); 144 | /* 145 | * after blocking for the first buffer, we can 146 | * keep reading until it blocks again, which 147 | * should exhaust the message. 148 | */ 149 | flags += MSG_DONTWAIT; 150 | } 151 | len += count; 152 | message = xrealloc(message, len + BUFSZ); 153 | memset(message + len, 0, BUFSZ); 154 | WARNX_DEBUG("%s: looping after count %zd\n", dpfx, count); 155 | firstloop = 0; 156 | } 157 | #ifdef SENDCMD_DEBUG 158 | free(dpfx); 159 | #endif 160 | *callerbuf = message; 161 | return len; 162 | } 163 | 164 | static ssize_t 165 | send_unix(int fd, char *buf, ssize_t sz) 166 | { 167 | ssize_t ret, off = 0; 168 | 169 | WARNX_DEBUG("entered send_unix with sz %zd\n", sz); 170 | 171 | while (sz > 0) { 172 | if (((ret = write(fd, buf + off, sz)) != sz) && ret == -1) { 173 | if (errno == EINTR) 174 | continue; 175 | warn("send_unix: bad write"); 176 | break; 177 | } 178 | sz -= ret; 179 | off += ret; 180 | } 181 | 182 | WARNX_DEBUG("leaving send_unix, off %zd, errno %d\n", off, errno); 183 | return off; 184 | } 185 | 186 | int 187 | send_command(int interactive, char *cmd) 188 | { 189 | struct sockaddr_un sun; 190 | char *wcmd, *response; 191 | char success = 0; 192 | ssize_t len; 193 | int fd; 194 | FILE *outf = NULL; 195 | 196 | #ifdef SENDCMD_DEBUG 197 | pid_t pid = getpid(); 198 | char *dpfx = xsprintf("send_command_%d", pid); 199 | #endif 200 | WARNX_DEBUG("%s: enter\n", dpfx); 201 | 202 | len = 1 + strlen(cmd) + 1; 203 | wcmd = xmalloc(len); 204 | *wcmd = (unsigned char)interactive; 205 | strncpy(wcmd + 1, cmd, len - 1); 206 | 207 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 208 | err(1, "socket"); 209 | 210 | if (strlen(rp_glob_screen.control_socket_path) >= sizeof(sun.sun_path)) 211 | err(1, "control socket path too long: %s", 212 | rp_glob_screen.control_socket_path); 213 | 214 | strncpy(sun.sun_path, rp_glob_screen.control_socket_path, 215 | sizeof(sun.sun_path)-1); 216 | sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; 217 | sun.sun_family = AF_UNIX; 218 | 219 | if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 220 | err(1, "failed to connect to control socket at %s", 221 | rp_glob_screen.control_socket_path); 222 | 223 | if (send_unix(fd, wcmd, len) != len) 224 | err(1, "%s: aborting after bad write", __func__); 225 | 226 | free(wcmd); 227 | 228 | if ((len = recv_unix(fd, &response)) == -1) 229 | warnx("send_message: aborted reply from receiver"); 230 | 231 | /* first byte is exit status */ 232 | success = *response; 233 | outf = success ? stdout : stderr; 234 | 235 | if (len > 1) { 236 | /* command had some output */ 237 | if (response[len] != '\0') { 238 | /* should not be possible, TODO remove */ 239 | warnx("%s\n", "last byte of response not null"); 240 | response[len] = '\0'; 241 | } 242 | fprintf(outf, "%s", response + 1); 243 | fflush(outf); 244 | } 245 | free(response); 246 | 247 | WARNX_DEBUG("%s: no more bytes\n", dpfx); 248 | #ifdef SENDCMD_DEBUG 249 | free(dpfx); 250 | #endif 251 | 252 | return success; 253 | } 254 | 255 | void 256 | receive_command(void) 257 | { 258 | cmdret *cmd_ret; 259 | char *result, *rcmd, *cmd; 260 | int cl, len = 0, interactive = 0; 261 | 262 | PRINT_DEBUG(("have connection waiting on command socket\n")); 263 | 264 | if ((cl = accept(rp_glob_screen.control_socket_fd, NULL, NULL)) == -1) { 265 | warn("accept"); 266 | return; 267 | } 268 | if ((fcntl(cl, F_SETFD, FD_CLOEXEC)) == -1) { 269 | warn("cloexec"); 270 | close(cl); 271 | return; 272 | } 273 | 274 | if ((len = recv_unix(cl, &cmd)) <= 1) { 275 | warnx("receive_command: %s\n", 276 | (len == -1 ? "encountered error during receive" 277 | : "received command was malformed")); 278 | goto done; 279 | } 280 | if (cmd[len] != '\0') { 281 | /* should not be possible, TODO remove */ 282 | warnx("%s\n", "last byte of sent command not null"); 283 | cmd[len] = '\0'; 284 | } 285 | interactive = cmd[0]; 286 | rcmd = cmd + 1; 287 | 288 | PRINT_DEBUG(("read %d byte(s) on command socket: %s\n", len, rcmd)); 289 | 290 | cmd_ret = command(interactive, rcmd); 291 | 292 | /* notify the client of any text that was returned by the command */ 293 | len = 2; 294 | if (cmd_ret->output) { 295 | result = xsprintf("%c%s", cmd_ret->success ? 1 : 0, 296 | cmd_ret->output); 297 | len = 1 + strlen(cmd_ret->output) + 1; 298 | } else if (cmd_ret->success) 299 | result = xsprintf("%c", 1); 300 | else 301 | result = xsprintf("%c", 0); 302 | 303 | cmdret_free(cmd_ret); 304 | 305 | PRINT_DEBUG(("writing back %d to command client: %s", len, result + 1)); 306 | 307 | if (send_unix(cl, result, len) != len) 308 | warnx("%s: proceeding after bad write", __func__); 309 | 310 | PRINT_DEBUG(("receive_command: write finished, closing\n")); 311 | done: 312 | free(cmd); 313 | close(cl); 314 | } 315 | -------------------------------------------------------------------------------- /communications.h: -------------------------------------------------------------------------------- 1 | /* 2 | * communications.h -- Send commands to a running copy of sdorfehs. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_COMMUNICATIONS_H 21 | #define _SDORFEHS_COMMUNICATIONS_H 1 22 | 23 | void init_control_socket_path(void); 24 | void listen_for_commands(void); 25 | int send_command(int interactive, char *cmd); 26 | void receive_command(void); 27 | 28 | #endif /* ! _SDORFEHS_COMMUNICATIONS_H */ 29 | -------------------------------------------------------------------------------- /completions.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | /* Needed on Linux for strcasestr */ 20 | #define _GNU_SOURCE 21 | #include 22 | 23 | #include "sdorfehs.h" 24 | 25 | rp_completions * 26 | completions_new(completion_fn list_fn, enum completion_styles style) 27 | { 28 | rp_completions *c; 29 | 30 | c = xmalloc(sizeof(rp_completions)); 31 | 32 | INIT_LIST_HEAD(&c->completion_list); 33 | c->complete_fn = list_fn; 34 | c->last_match = NULL; 35 | c->partial = NULL; 36 | c->virgin = 1; 37 | c->style = style; 38 | 39 | return c; 40 | } 41 | 42 | void 43 | completions_free(rp_completions *c) 44 | { 45 | struct sbuf *cur; 46 | struct list_head *tmp, *iter; 47 | 48 | /* Clear our list */ 49 | list_for_each_safe_entry(cur, iter, tmp, &c->completion_list, node) { 50 | list_del(&cur->node); 51 | sbuf_free(cur); 52 | } 53 | 54 | /* Free the partial string. */ 55 | free(c->partial); 56 | 57 | free(c); 58 | } 59 | 60 | static void 61 | completions_assign(rp_completions *c, struct list_head *new_list) 62 | { 63 | struct sbuf *cur; 64 | struct list_head *tmp, *iter; 65 | 66 | /* Clear our list */ 67 | list_for_each_safe_entry(cur, iter, tmp, &c->completion_list, node) { 68 | list_del(&cur->node); 69 | sbuf_free(cur); 70 | } 71 | 72 | /* 73 | * splice the list into completion_list. Note that we SHOULDN'T free 74 | * new_list, because they share the same memory. 75 | */ 76 | INIT_LIST_HEAD(&c->completion_list); 77 | list_splice(new_list, &c->completion_list); 78 | 79 | list_first(c->last_match, &c->completion_list, node); 80 | } 81 | 82 | static void 83 | completions_update(rp_completions *c, char *partial) 84 | { 85 | struct list_head *new_list; 86 | 87 | new_list = c->complete_fn(partial); 88 | 89 | c->virgin = 0; 90 | free(c->partial); 91 | c->partial = xstrdup(partial); 92 | 93 | completions_assign(c, new_list); 94 | 95 | /* Free the head structure for our list. */ 96 | free(new_list); 97 | } 98 | 99 | /* 100 | * Return true if completion is an alternative for partial string, given the 101 | * style used. 102 | */ 103 | static int 104 | completions_match(rp_completions *c, char *completion, char *partial) 105 | { 106 | int match = 0; 107 | 108 | switch (c->style) { 109 | case BASIC: 110 | match = str_comp(completion, partial, strlen(partial)); 111 | break; 112 | case SUBSTRING: 113 | match = (strcasestr(completion, partial) != NULL); 114 | break; 115 | } 116 | 117 | return match; 118 | } 119 | 120 | static char * 121 | completions_prev_match(rp_completions *c) 122 | { 123 | struct sbuf *cur; 124 | 125 | /* 126 | * search forward from our last match through the list looking for 127 | * another match. 128 | */ 129 | for (cur = list_prev_entry(c->last_match, &c->completion_list, node); 130 | cur != c->last_match; 131 | cur = list_prev_entry(cur, &c->completion_list, node)) { 132 | if (completions_match(c, sbuf_get(cur), c->partial)) { 133 | /* 134 | * We found a match so update our last_match pointer 135 | * and return the string. 136 | */ 137 | c->last_match = cur; 138 | return sbuf_get(cur); 139 | } 140 | } 141 | 142 | return NULL; 143 | } 144 | 145 | static char * 146 | completions_next_match(rp_completions *c) 147 | { 148 | struct sbuf *cur; 149 | 150 | /* 151 | * search forward from our last match through the list looking for 152 | * another match. 153 | */ 154 | for (cur = list_next_entry(c->last_match, &c->completion_list, node); 155 | cur != c->last_match; 156 | cur = list_next_entry(cur, &c->completion_list, node)) { 157 | if (completions_match(c, sbuf_get(cur), c->partial)) { 158 | /* 159 | * We found a match so update our last_match pointer 160 | * and return the string. 161 | */ 162 | c->last_match = cur; 163 | return sbuf_get(cur); 164 | } 165 | } 166 | 167 | return NULL; 168 | } 169 | 170 | /* Return a completed string that starts with partial. */ 171 | char * 172 | completions_complete(rp_completions *c, char *partial, int direction) 173 | { 174 | if (c->virgin) { 175 | completions_update(c, partial); 176 | 177 | /* 178 | * Since it's never been completed on and c->last_match points 179 | * to the first element of the list which may be a match. So 180 | * check it. FIXME: This is a bit of a hack. 181 | */ 182 | if (c->last_match == NULL) 183 | return NULL; 184 | 185 | /* 186 | * c->last_match contains the first match in the forward 187 | * direction. So if we're looking for the previous match, then 188 | * check the previous element from last_match. 189 | */ 190 | if (direction == COMPLETION_PREVIOUS) 191 | c->last_match = list_prev_entry(c->last_match, 192 | &c->completion_list, node); 193 | 194 | /* Now check if last_match is a match for partial. */ 195 | if (completions_match(c, sbuf_get(c->last_match), c->partial)) 196 | return sbuf_get(c->last_match); 197 | } 198 | if (c->last_match == NULL) 199 | return NULL; 200 | 201 | /* Depending on the direction, find our "next" match. */ 202 | if (direction == COMPLETION_NEXT) 203 | return completions_next_match(c); 204 | 205 | /* Otherwise get the previous match */ 206 | return completions_prev_match(c); 207 | } 208 | -------------------------------------------------------------------------------- /completions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef _SDORFEHS_COMPLETIONS_H 20 | #define _SDORFEHS_COMPLETIONS_H 1 21 | 22 | char *completions_complete(rp_completions * c, char *partial, int direction); 23 | rp_completions *completions_new(completion_fn list_fn, 24 | enum completion_styles style); 25 | void completions_free(rp_completions * c); 26 | 27 | #endif /* ! _SDORFEHS_COMPLETIONS_H */ 28 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Config file. Edit these values and recompile. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_CONF_H 21 | #define _SDORFEHS_CONF_H 1 22 | 23 | #include "data.h" 24 | #include "actions.h" 25 | 26 | #define PROGNAME "sdorfehs" 27 | 28 | /* Enter command mode with Control+a by default. */ 29 | #define KEY_PREFIX XK_a 30 | #define MODIFIER_PREFIX RP_CONTROL_MASK 31 | 32 | /* Terminal executed by default. */ 33 | #define TERM_PROG "xterm" 34 | 35 | /* This is the abort key when typing input. */ 36 | #define INPUT_ABORT_KEY XK_g 37 | #define INPUT_ABORT_MODIFIER RP_CONTROL_MASK 38 | 39 | /* This is the previous history entry key when typing input. */ 40 | #define INPUT_PREV_HISTORY_KEY XK_p 41 | #define INPUT_PREV_HISTORY_MODIFIER RP_CONTROL_MASK 42 | 43 | /* This is the next history entry key when typing input. */ 44 | #define INPUT_NEXT_HISTORY_KEY XK_n 45 | #define INPUT_NEXT_HISTORY_MODIFIER RP_CONTROL_MASK 46 | 47 | /* Key used to enlarge frame vertically when in resize mode. */ 48 | #define RESIZE_VGROW_KEY XK_n 49 | #define RESIZE_VGROW_MODIFIER RP_CONTROL_MASK 50 | 51 | /* Key used to shrink frame vertically when in resize mode. */ 52 | #define RESIZE_VSHRINK_KEY XK_p 53 | #define RESIZE_VSHRINK_MODIFIER RP_CONTROL_MASK 54 | 55 | /* Key used to enlarge frame horizontally when in resize mode. */ 56 | #define RESIZE_HGROW_KEY XK_f 57 | #define RESIZE_HGROW_MODIFIER RP_CONTROL_MASK 58 | 59 | /* Key used to shrink frame horizontally when in resize mode. */ 60 | #define RESIZE_HSHRINK_KEY XK_b 61 | #define RESIZE_HSHRINK_MODIFIER RP_CONTROL_MASK 62 | 63 | /* Key used to shrink frame to fit it's current window. */ 64 | #define RESIZE_SHRINK_TO_WINDOW_KEY XK_s 65 | #define RESIZE_SHRINK_TO_WINDOW_MODIFIER 0 66 | 67 | /* Key used to exit resize mode. */ 68 | #define RESIZE_END_KEY XK_Return 69 | #define RESIZE_END_MODIFIER 0 70 | 71 | /* Number of history items to store. */ 72 | #define INPUT_MAX_HISTORY 50 73 | 74 | /* 75 | * Treat windows with maxsize hints as if they were a transient window (don't 76 | * hide the windows underneath, and center them) 77 | */ 78 | #define MAXSIZE_WINDOWS_ARE_TRANSIENTS 79 | 80 | /* 81 | * Treat windows with aspect hints as if they were a transient window (don't 82 | * hide the windows underneath, and center them) 83 | */ 84 | #define ASPECT_WINDOWS_ARE_TRANSIENTS 85 | 86 | /* 87 | * An alias command could recursively call inself infinitely. This stops that 88 | * behavior. 89 | */ 90 | #define MAX_ALIAS_RECURSIVE_DEPTH 16 91 | 92 | /* Maximum depth of a link. Used in the 'link' command. */ 93 | #define MAX_LINK_DEPTH 16 94 | 95 | /* 96 | * Bad window messages can be safely ignored now that we have become stable 97 | * enough. Comment this line if you wish to be notified about bad window 98 | * messages. 99 | */ 100 | #define IGNORE_BADWINDOW 1 101 | 102 | /* This is the name of the first vscreen that is created. */ 103 | #define DEFAULT_VSCREEN_NAME "default" 104 | 105 | /* Maximum allowed history size */ 106 | #define MAX_HISTORY_SIZE 100 107 | 108 | /* The default filename in which to store the history */ 109 | #define HISTORY_FILE "history" 110 | 111 | /* The name of the root keymap */ 112 | #define ROOT_KEYMAP "root" 113 | 114 | /* The name of the top level keymap */ 115 | #define TOP_KEYMAP "top" 116 | 117 | /* The default font */ 118 | #define DEFAULT_XFT_FONT "monospace:size=12" 119 | 120 | #endif /* !_ _SDORFEHS_CONF_H */ 121 | -------------------------------------------------------------------------------- /data.h: -------------------------------------------------------------------------------- 1 | /* 2 | * our datatypes and global variables 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_DATA_H 21 | #define _SDORFEHS_DATA_H 22 | 23 | #include "linkedlist.h" 24 | #include "number.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | typedef struct rp_window rp_window; 32 | typedef struct rp_screen rp_screen; 33 | typedef struct rp_global_screen rp_global_screen; 34 | typedef struct rp_vscreen rp_vscreen; 35 | typedef struct rp_action rp_action; 36 | typedef struct rp_keymap rp_keymap; 37 | typedef struct rp_frame rp_frame; 38 | typedef struct rp_child_info rp_child_info; 39 | typedef struct rp_window_elem rp_window_elem; 40 | typedef struct rp_completions rp_completions; 41 | typedef struct rp_input_line rp_input_line; 42 | 43 | enum rp_edge { 44 | EDGE_TOP = (1 << 1), 45 | EDGE_LEFT = (1 << 2), 46 | EDGE_RIGHT = (1 << 3), 47 | EDGE_BOTTOM = (1 << 4), 48 | }; 49 | 50 | struct rp_frame { 51 | rp_vscreen *vscreen; 52 | 53 | int number; 54 | int x, y, width, height; 55 | 56 | /* The number of the window that is focused in this frame. */ 57 | int win_number; 58 | 59 | /* The number of the window to focus when restoring this frame. */ 60 | int restore_win_number; 61 | 62 | /* For determining the last frame. */ 63 | int last_access; 64 | 65 | /* 66 | * Boolean that is set when a frame is `dedicated' (a.k.a. glued) to 67 | * one window. 68 | */ 69 | unsigned int dedicated; 70 | 71 | /* Whether this frame is touching an edge before a screen update */ 72 | enum rp_edge edges; 73 | 74 | struct list_head node; 75 | }; 76 | 77 | struct rp_window { 78 | rp_vscreen *vscreen; 79 | Window w; 80 | int state; 81 | int last_access; 82 | int named; 83 | 84 | /* 85 | * A number uniquely identifying this window. This is a different 86 | * number than the one given to it by the vscreen it is in. This number 87 | * is used for internal purposes, whereas the vscreen number is what 88 | * the user sees. 89 | */ 90 | int number; 91 | 92 | /* Window name hints. */ 93 | char *user_name; 94 | char *wm_name; 95 | char *res_name; 96 | char *res_class; 97 | 98 | /* Dimensions */ 99 | int x, y, width, height, border, full_screen; 100 | 101 | /* WM Hints */ 102 | XSizeHints *hints; 103 | 104 | /* Colormap */ 105 | Colormap colormap; 106 | 107 | /* Is this a transient window? */ 108 | int transient; 109 | Window transient_for; 110 | 111 | /* Saved mouse position */ 112 | int mouse_x, mouse_y; 113 | 114 | /* 115 | * The alignment of the window. Decides to what side or corner the 116 | * window sticks to. 117 | */ 118 | int gravity; 119 | 120 | /* 121 | * A window can be visible inside a frame but not the frame's current 122 | * window. This keeps track of what frame the window was mapped into. 123 | */ 124 | int frame_number; 125 | 126 | /* The frame number we want to remain in */ 127 | int sticky_frame; 128 | 129 | /* 130 | * Sometimes a window is intended for a certain frame. When a window is 131 | * mapped and this is >0 then use the frame (if it exists). 132 | */ 133 | int intended_frame_number; 134 | 135 | struct list_head node; 136 | }; 137 | 138 | struct rp_window_elem { 139 | rp_window *win; 140 | int number; 141 | struct list_head node; 142 | }; 143 | 144 | struct rp_global_screen { 145 | Window root, wm_check; 146 | unsigned long fgcolor, bgcolor, fwcolor, bwcolor, bar_bordercolor; 147 | 148 | /* This numset is responsible for giving out numbers for each screen */ 149 | struct numset *numset; 150 | 151 | /* The path to and open fd of our control socket */ 152 | char *control_socket_path; 153 | int control_socket_fd; 154 | 155 | /* The path to and open fd of our bar FIFO */ 156 | char *bar_fifo_path; 157 | int bar_fifo_fd; 158 | }; 159 | 160 | struct xrandr_info { 161 | int output; 162 | int crtc; 163 | int primary; 164 | char *name; 165 | }; 166 | 167 | struct rp_vscreen { 168 | rp_screen *screen; 169 | 170 | /* Virtual screen number, handled by rp_screen's vscreens_numset */ 171 | int number; 172 | 173 | /* Name */ 174 | char *name; 175 | 176 | /* For determining the last vscreen. */ 177 | int last_access; 178 | 179 | /* 180 | * A list of frames that may or may not contain windows. There should 181 | * always be one in the list. 182 | */ 183 | struct list_head frames; 184 | 185 | /* Keep track of which numbers have been given to frames. */ 186 | struct numset *frames_numset; 187 | 188 | /* 189 | * The number of the currently focused frame. One for each vscreen so 190 | * when you switch vscreens the focus doesn't get frobbed. 191 | */ 192 | int current_frame; 193 | 194 | /* The list of windows participating in this vscreen. */ 195 | struct list_head mapped_windows, unmapped_windows; 196 | 197 | /* 198 | * This numset is responsible for giving out numbers for each window in 199 | * the vscreen. 200 | */ 201 | struct numset *numset; 202 | 203 | struct list_head node; 204 | }; 205 | 206 | struct rp_font { 207 | char *name; 208 | XftFont *font; 209 | }; 210 | 211 | struct rp_screen { 212 | GC normal_gc, inverse_gc; 213 | Window root, bar_window, key_window, input_window, frame_window, 214 | help_window; 215 | int bar_is_raised; 216 | int screen_num; /* Our screen number as dictated by X */ 217 | Colormap def_cmap; 218 | Cursor rat; 219 | 220 | /* Screen number, handled by rp_global_screen numset */ 221 | int number; 222 | 223 | struct xrandr_info xrandr; 224 | 225 | /* Here to abstract over the Xrandr vs X screens difference */ 226 | int left, top, width, height; 227 | 228 | char *display_string; 229 | 230 | /* Used by sfrestore */ 231 | struct sbuf *scratch_buffer; 232 | 233 | XftFont *xft_font; 234 | struct rp_font xft_font_cache[5]; 235 | XftColor xft_fgcolor, xft_bgcolor; 236 | 237 | struct list_head vscreens; 238 | struct numset *vscreens_numset; 239 | rp_vscreen *current_vscreen; 240 | 241 | rp_window *full_screen_win; 242 | 243 | struct sbuf *bar_text; 244 | 245 | /* This structure can exist in a list. */ 246 | struct list_head node; 247 | }; 248 | 249 | struct rp_action { 250 | KeySym key; 251 | unsigned int state; 252 | char *data; /* misc data to be passed to the function */ 253 | /* void (*func)(void *); */ 254 | }; 255 | 256 | struct rp_keymap { 257 | char *name; 258 | rp_action *actions; 259 | int actions_last; 260 | int actions_size; 261 | 262 | /* This structure can be part of a list. */ 263 | struct list_head node; 264 | }; 265 | 266 | struct rp_key { 267 | KeySym sym; 268 | unsigned int state; 269 | }; 270 | 271 | struct rp_defaults { 272 | /* 273 | * Default positions for new normal windows, transient windows, and 274 | * normal windows with maxsize hints. 275 | */ 276 | int win_gravity; 277 | int trans_gravity; 278 | int maxsize_gravity; 279 | 280 | int input_window_size; 281 | int window_border_width; 282 | int only_border; 283 | 284 | int bar_x_padding; 285 | int bar_y_padding; 286 | int bar_location; 287 | int bar_timeout; 288 | int bar_border_width; 289 | int bar_in_padding; 290 | int bar_sticky; 291 | 292 | int frame_indicator_timeout; 293 | int frame_resize_unit; 294 | 295 | int padding_left; 296 | int padding_right; 297 | int padding_top; 298 | int padding_bottom; 299 | 300 | char *font_string; 301 | 302 | char *fgcolor_string; 303 | char *bgcolor_string; 304 | char *fwcolor_string; 305 | char *bwcolor_string; 306 | char *barbordercolor_string; 307 | 308 | int wait_for_key_cursor; 309 | 310 | char *window_fmt; 311 | char *info_fmt; 312 | char *sticky_fmt; 313 | char *resize_fmt; 314 | 315 | /* Which name to use: wm_name, res_name, res_class. */ 316 | int win_name; 317 | 318 | int startup_message; 319 | 320 | /* 321 | * Decides whether the window list is displayed in a row or a column. 322 | */ 323 | int window_list_style; 324 | 325 | /* Pointer warping toggle. */ 326 | int warp; 327 | 328 | int history_size; 329 | /* remove older history when adding the same again */ 330 | int history_compaction; 331 | /* expand ! when compiled with libhistory */ 332 | int history_expansion; 333 | 334 | char *frame_selectors; 335 | 336 | /* How many frame sets to remember when undoing. */ 337 | int maxundos; 338 | 339 | /* The name of the top level keymap */ 340 | char *top_kmap; 341 | 342 | /* Frame indicator format */ 343 | char *frame_fmt; 344 | 345 | /* Number of virtual screens */ 346 | int vscreens; 347 | 348 | /* Window gap */ 349 | int gap; 350 | 351 | /* Whether to ignore window size hints */ 352 | int ignore_resize_hints; 353 | 354 | /* New mapped window always uses current vscreen */ 355 | int win_add_cur_vscreen; 356 | }; 357 | 358 | /* Information about a child process. */ 359 | struct rp_child_info { 360 | /* The command that was executed. */ 361 | char *cmd; 362 | 363 | /* PID of the process. */ 364 | int pid; 365 | 366 | /* Return status when the child process finished. */ 367 | int status; 368 | 369 | /* When this is != 0 then the process finished. */ 370 | int terminated; 371 | 372 | /* what was current when it was launched? */ 373 | rp_frame *frame; 374 | rp_screen *screen; 375 | rp_vscreen *vscreen; 376 | 377 | /* 378 | * Non-zero when the pid has mapped a window. This is to prevent every 379 | * window the program opens from getting mapped in the frame it was 380 | * launched from. Only the first window should do this. 381 | */ 382 | int window_mapped; 383 | 384 | /* This structure can exist in a list. */ 385 | struct list_head node; 386 | }; 387 | 388 | /* 389 | * These defines should be used to specify the modifier mask for keys and they 390 | * are translated into the X11 modifier mask when the time comes to compare 391 | * modifier masks. 392 | */ 393 | #define RP_SHIFT_MASK 1 394 | #define RP_CONTROL_MASK 2 395 | #define RP_META_MASK 4 396 | #define RP_ALT_MASK 8 397 | #define RP_SUPER_MASK 16 398 | #define RP_HYPER_MASK 32 399 | 400 | struct modifier_info { 401 | /* unsigned int mode_switch_mask; */ 402 | unsigned int meta_mod_mask; 403 | unsigned int alt_mod_mask; 404 | unsigned int super_mod_mask; 405 | unsigned int hyper_mod_mask; 406 | 407 | /* 408 | * Keep track of these because they mess up the grab and should be 409 | * ignored. 410 | */ 411 | unsigned int num_lock_mask; 412 | unsigned int scroll_lock_mask; 413 | }; 414 | 415 | typedef struct list_head *(*completion_fn) (char *string); 416 | 417 | /* 418 | BASIC: The completion shall begin with the same characters as the partial 419 | string. Case is ignored. 420 | 421 | SUBSTRING: The partial string shall be a subpart of the completion. Case 422 | is ignored. 423 | */ 424 | enum completion_styles { 425 | BASIC, 426 | SUBSTRING 427 | }; 428 | 429 | struct rp_completions { 430 | /* 431 | * A pointer to the partial string that is being completed. We need to 432 | * store this so that the user can cycle through all possible 433 | * completions. 434 | */ 435 | char *partial; 436 | 437 | /* 438 | * A pointer to the string that was last matched string. Used to keep 439 | * track of where we are in the completion list. 440 | */ 441 | struct sbuf *last_match; 442 | 443 | /* A list of sbuf's which are possible completions. */ 444 | struct list_head completion_list; 445 | 446 | /* The function that generates the completions. */ 447 | completion_fn complete_fn; 448 | 449 | /* 450 | * virgin = 1 means no completions have been attempted on the input 451 | * string. 452 | */ 453 | unsigned short int virgin; 454 | 455 | /* The completion style used to perform string comparisons */ 456 | enum completion_styles style; 457 | }; 458 | 459 | struct rp_input_line { 460 | char *buffer; 461 | char *prompt; 462 | char *saved; 463 | size_t position; 464 | size_t length; 465 | size_t size; 466 | rp_completions *compl; 467 | Atom selection; 468 | int history_id; 469 | }; 470 | 471 | /* The hook dictionary. */ 472 | struct rp_hook_db_entry { 473 | char *name; 474 | struct list_head *hook; 475 | }; 476 | 477 | typedef struct rp_xselection rp_xselection; 478 | struct rp_xselection { 479 | char *text; 480 | int len; 481 | }; 482 | 483 | #endif /* _SDORFEHS_DATA_H */ 484 | -------------------------------------------------------------------------------- /editor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "sdorfehs.h" 30 | 31 | /* bind functions */ 32 | static edit_status editor_forward_char(rp_input_line *line); 33 | static edit_status editor_backward_char(rp_input_line *line); 34 | static edit_status editor_forward_word(rp_input_line *line); 35 | static edit_status editor_backward_word(rp_input_line *line); 36 | static edit_status editor_beginning_of_line(rp_input_line *line); 37 | static edit_status editor_end_of_line(rp_input_line *line); 38 | static edit_status editor_delete_char(rp_input_line *line); 39 | static edit_status editor_backward_delete_char(rp_input_line *line); 40 | static edit_status editor_kill_word(rp_input_line *line); 41 | static edit_status editor_backward_kill_word(rp_input_line *line); 42 | static edit_status editor_kill_line(rp_input_line *line); 43 | static edit_status editor_paste_selection(rp_input_line *line); 44 | static edit_status editor_abort(rp_input_line *line); 45 | static edit_status editor_no_action(rp_input_line *line); 46 | static edit_status editor_enter(rp_input_line *line); 47 | static edit_status editor_history_previous(rp_input_line *line); 48 | static edit_status editor_history_next(rp_input_line *line); 49 | static edit_status editor_backward_kill_line(rp_input_line *line); 50 | static edit_status editor_complete_prev(rp_input_line *line); 51 | static edit_status editor_complete_next(rp_input_line *line); 52 | 53 | /* default edit action */ 54 | static edit_status editor_insert(rp_input_line *line, char *keysym_buf); 55 | 56 | static char *saved_command = NULL; 57 | 58 | typedef struct edit_binding edit_binding; 59 | 60 | struct edit_binding { 61 | struct rp_key key; 62 | edit_status(*func) (rp_input_line *); 63 | }; 64 | 65 | static edit_binding edit_bindings[] = 66 | {{{XK_g, RP_CONTROL_MASK}, editor_abort}, 67 | {{XK_Escape, 0}, editor_abort}, 68 | {{XK_f, RP_CONTROL_MASK}, editor_forward_char}, 69 | {{XK_Right, 0}, editor_forward_char}, 70 | {{XK_b, RP_CONTROL_MASK}, editor_backward_char}, 71 | {{XK_Left, 0}, editor_backward_char}, 72 | {{XK_f, RP_META_MASK}, editor_forward_word}, 73 | {{XK_b, RP_META_MASK}, editor_backward_word}, 74 | {{XK_a, RP_CONTROL_MASK}, editor_beginning_of_line}, 75 | {{XK_Home, 0}, editor_beginning_of_line}, 76 | {{XK_e, RP_CONTROL_MASK}, editor_end_of_line}, 77 | {{XK_End, 0}, editor_end_of_line}, 78 | {{XK_d, RP_CONTROL_MASK}, editor_delete_char}, 79 | {{XK_Delete, 0}, editor_delete_char}, 80 | {{XK_BackSpace, 0}, editor_backward_delete_char}, 81 | {{XK_h, RP_CONTROL_MASK}, editor_backward_delete_char}, 82 | {{XK_BackSpace, RP_META_MASK}, editor_backward_kill_word}, 83 | {{XK_d, RP_META_MASK}, editor_kill_word}, 84 | {{XK_k, RP_CONTROL_MASK}, editor_kill_line}, 85 | {{XK_u, RP_CONTROL_MASK}, editor_backward_kill_line}, 86 | {{XK_y, RP_CONTROL_MASK}, editor_paste_selection}, 87 | {{XK_p, RP_CONTROL_MASK}, editor_history_previous}, 88 | {{XK_Up, 0}, editor_history_previous}, 89 | {{XK_n, RP_CONTROL_MASK}, editor_history_next}, 90 | {{XK_Down, 0}, editor_history_next}, 91 | {{XK_Return, 0}, editor_enter}, 92 | {{XK_m, RP_CONTROL_MASK}, editor_enter}, 93 | {{XK_KP_Enter, 0}, editor_enter}, 94 | {{XK_Tab, 0}, editor_complete_next}, 95 | {{XK_ISO_Left_Tab, 0}, editor_complete_prev}, 96 | {{0, 0}, 0}, 97 | }; 98 | 99 | rp_input_line * 100 | input_line_new(char *prompt, char *preinput, int history_id, 101 | enum completion_styles style, completion_fn fn) 102 | { 103 | rp_input_line *line; 104 | size_t length; 105 | 106 | line = xmalloc(sizeof(rp_input_line)); 107 | line->prompt = prompt; 108 | line->compl = completions_new(fn, style); 109 | line->history_id = history_id; 110 | 111 | /* Allocate some memory to start with (100 extra bytes) */ 112 | length = strlen(preinput); 113 | line->size = length + 1 + 100; 114 | line->buffer = xmalloc(line->size); 115 | 116 | /* load in the preinput */ 117 | memcpy(line->buffer, preinput, length); 118 | line->buffer[length] = '\0'; 119 | line->position = line->length = length; 120 | 121 | return line; 122 | } 123 | 124 | void 125 | input_line_free(rp_input_line *line) 126 | { 127 | completions_free(line->compl); 128 | free(line->buffer); 129 | free(line); 130 | } 131 | 132 | edit_status 133 | execute_edit_action(rp_input_line *line, KeySym ch, unsigned int modifier, 134 | char *keysym_buf) 135 | { 136 | struct edit_binding *binding = NULL; 137 | int found_binding = 0; 138 | edit_status status; 139 | 140 | for (binding = edit_bindings; binding->func; binding++) { 141 | if (ch == binding->key.sym && modifier == binding->key.state) { 142 | found_binding = 1; 143 | break; 144 | } 145 | } 146 | 147 | if (found_binding) 148 | status = binding->func(line); 149 | else if (modifier && modifier != RP_SHIFT_MASK) 150 | status = editor_no_action(line); 151 | else 152 | status = editor_insert(line, keysym_buf); 153 | 154 | return status; 155 | } 156 | 157 | static edit_status 158 | editor_forward_char(rp_input_line *line) 159 | { 160 | if (line->position == line->length) 161 | return EDIT_NO_OP; 162 | 163 | if (isu8start(line->buffer[line->position])) { 164 | do 165 | line->position++; 166 | while (isu8cont(line->buffer[line->position])); 167 | } else 168 | line->position++; 169 | 170 | return EDIT_MOVE; 171 | } 172 | 173 | static edit_status 174 | editor_backward_char(rp_input_line *line) 175 | { 176 | if (line->position == 0) 177 | return EDIT_NO_OP; 178 | 179 | do { 180 | line->position--; 181 | } while (line->position > 0 && isu8cont(line->buffer[line->position])); 182 | 183 | return EDIT_MOVE; 184 | } 185 | 186 | static edit_status 187 | editor_forward_word(rp_input_line *line) 188 | { 189 | if (line->position == line->length) 190 | return EDIT_NO_OP; 191 | 192 | while (line->position < line->length 193 | && !isalnum((unsigned char) line->buffer[line->position])) 194 | line->position++; 195 | 196 | while (line->position < line->length 197 | && (isalnum((unsigned char) line->buffer[line->position]) 198 | || isu8char(line->buffer[line->position]))) 199 | line->position++; 200 | 201 | return EDIT_MOVE; 202 | } 203 | 204 | static edit_status 205 | editor_backward_word(rp_input_line *line) 206 | { 207 | if (line->position == 0) 208 | return EDIT_NO_OP; 209 | 210 | while (line->position > 0 && 211 | !isalnum((unsigned char)line->buffer[line->position])) 212 | line->position--; 213 | 214 | while (line->position > 0 && 215 | (isalnum((unsigned char)line->buffer[line->position]) 216 | || isu8char(line->buffer[line->position]))) 217 | line->position--; 218 | 219 | return EDIT_MOVE; 220 | } 221 | 222 | static edit_status 223 | editor_beginning_of_line(rp_input_line *line) 224 | { 225 | if (line->position == 0) 226 | return EDIT_NO_OP; 227 | else { 228 | line->position = 0; 229 | return EDIT_MOVE; 230 | } 231 | } 232 | 233 | static edit_status 234 | editor_end_of_line(rp_input_line *line) 235 | { 236 | if (line->position == line->length) 237 | return EDIT_NO_OP; 238 | else { 239 | line->position = line->length; 240 | return EDIT_MOVE; 241 | } 242 | } 243 | 244 | static edit_status 245 | editor_delete_char(rp_input_line *line) 246 | { 247 | size_t diff = 0; 248 | 249 | if (line->position == line->length) 250 | return EDIT_NO_OP; 251 | 252 | if (isu8start(line->buffer[line->position])) { 253 | do { 254 | diff++; 255 | } while (isu8cont(line->buffer[line->position + diff])); 256 | } else 257 | diff++; 258 | 259 | memmove(&line->buffer[line->position], 260 | &line->buffer[line->position + diff], 261 | line->length - line->position + diff + 1); 262 | 263 | line->length -= diff; 264 | 265 | return EDIT_DELETE; 266 | } 267 | 268 | static edit_status 269 | editor_backward_delete_char(rp_input_line *line) 270 | { 271 | size_t diff = 1; 272 | 273 | if (line->position == 0) 274 | return EDIT_NO_OP; 275 | 276 | while (line->position - diff > 0 && 277 | isu8cont(line->buffer[line->position - diff])) 278 | diff++; 279 | 280 | memmove(&line->buffer[line->position - diff], 281 | &line->buffer[line->position], 282 | line->length - line->position + 1); 283 | 284 | line->position -= diff; 285 | line->length -= diff; 286 | 287 | return EDIT_DELETE; 288 | } 289 | 290 | static edit_status 291 | editor_kill_word(rp_input_line *line) 292 | { 293 | size_t diff = 0; 294 | 295 | if (line->position == line->length) 296 | return EDIT_NO_OP; 297 | 298 | while (line->position + diff < line->length && 299 | !isalnum((unsigned char) line->buffer[line->position + diff])) 300 | diff++; 301 | 302 | while (line->position + diff < line->length 303 | && (isalnum((unsigned char) line->buffer[line->position + diff]) 304 | || isu8char(line->buffer[line->position + diff]))) 305 | diff++; 306 | 307 | /* Add the word to the X11 selection. */ 308 | set_nselection(&line->buffer[line->position], diff); 309 | 310 | memmove(&line->buffer[line->position], 311 | &line->buffer[line->position + diff], 312 | line->length - line->position + diff + 1); 313 | 314 | line->length -= diff; 315 | 316 | return EDIT_DELETE; 317 | } 318 | 319 | static edit_status 320 | editor_backward_kill_word(rp_input_line *line) 321 | { 322 | size_t diff = 1; 323 | 324 | if (line->position == 0) 325 | return EDIT_NO_OP; 326 | 327 | while (line->position - diff > 0 && 328 | !isalnum((unsigned char) line->buffer[line->position - diff])) 329 | diff++; 330 | 331 | while (line->position - diff > 0 332 | && (isalnum((unsigned char) line->buffer[line->position - diff]) 333 | || isu8char(line->buffer[line->position - diff]))) 334 | diff++; 335 | 336 | /* Add the word to the X11 selection. */ 337 | set_nselection(&line->buffer[line->position - diff], diff); 338 | 339 | memmove(&line->buffer[line->position - diff], 340 | &line->buffer[line->position], 341 | line->length - line->position + 1); 342 | 343 | line->position -= diff; 344 | line->length -= diff; 345 | 346 | return EDIT_DELETE; 347 | } 348 | 349 | static edit_status 350 | editor_kill_line(rp_input_line *line) 351 | { 352 | if (line->position == line->length) 353 | return EDIT_NO_OP; 354 | 355 | /* Add the line to the X11 selection. */ 356 | set_selection(&line->buffer[line->position]); 357 | 358 | line->length = line->position; 359 | line->buffer[line->length] = '\0'; 360 | 361 | return EDIT_DELETE; 362 | } 363 | 364 | /* Do the dirty work of killing a line backwards. */ 365 | static void 366 | backward_kill_line(rp_input_line *line) 367 | { 368 | memmove(&line->buffer[0], &line->buffer[line->position], 369 | line->length - line->position + 1); 370 | 371 | line->length -= line->position; 372 | line->position = 0; 373 | } 374 | 375 | static edit_status 376 | editor_backward_kill_line(rp_input_line *line) 377 | { 378 | if (line->position == 0) 379 | return EDIT_NO_OP; 380 | 381 | /* Add the line to the X11 selection. */ 382 | set_nselection(line->buffer, line->position); 383 | 384 | backward_kill_line(line); 385 | 386 | return EDIT_DELETE; 387 | } 388 | 389 | static edit_status 390 | editor_history_previous(rp_input_line *line) 391 | { 392 | const char *entry = history_previous(line->history_id); 393 | 394 | if (entry) { 395 | if (!saved_command) { 396 | line->buffer[line->length] = '\0'; 397 | saved_command = xstrdup(line->buffer); 398 | PRINT_DEBUG(("saved current command line: \'%s\'\n", 399 | saved_command)); 400 | } 401 | free(line->buffer); 402 | line->buffer = xstrdup(entry); 403 | line->length = strlen(line->buffer); 404 | line->size = line->length + 1; 405 | line->position = line->length; 406 | PRINT_DEBUG(("entry: \'%s\'\n", line->buffer)); 407 | } else { 408 | PRINT_DEBUG(("- do nothing -\n")); 409 | return EDIT_NO_OP; 410 | } 411 | 412 | return EDIT_INSERT; 413 | } 414 | 415 | static edit_status 416 | editor_history_next(rp_input_line *line) 417 | { 418 | const char *entry = history_next(line->history_id); 419 | 420 | if (entry) { 421 | free(line->buffer); 422 | line->buffer = xstrdup(entry); 423 | PRINT_DEBUG(("entry: \'%s\'\n", line->buffer)); 424 | } else if (saved_command) { 425 | free(line->buffer); 426 | line->buffer = saved_command; 427 | saved_command = NULL; 428 | PRINT_DEBUG(("restored command line: \'%s\'\n", line->buffer)); 429 | } else { 430 | PRINT_DEBUG(("- do nothing -\n")); 431 | return EDIT_NO_OP; 432 | } 433 | 434 | line->length = strlen(line->buffer); 435 | line->size = line->length + 1; 436 | line->position = line->length; 437 | 438 | return EDIT_INSERT; 439 | } 440 | 441 | static edit_status 442 | editor_abort(rp_input_line *line) 443 | { 444 | return EDIT_ABORT; 445 | } 446 | 447 | static edit_status 448 | editor_no_action(rp_input_line *line) 449 | { 450 | return EDIT_NO_OP; 451 | } 452 | 453 | static edit_status 454 | editor_insert(rp_input_line *line, char *keysym_buf) 455 | { 456 | size_t nbytes; 457 | 458 | PRINT_DEBUG(("keysym_buf: '%s'\n", keysym_buf)); 459 | 460 | nbytes = strlen(keysym_buf); 461 | if (line->length + nbytes > line->size - 1) { 462 | line->size += nbytes + 100; 463 | line->buffer = xrealloc(line->buffer, line->size); 464 | } 465 | memmove(&line->buffer[line->position + nbytes], 466 | &line->buffer[line->position], 467 | line->length - line->position + 1); 468 | memcpy(&line->buffer[line->position], keysym_buf, nbytes); 469 | 470 | line->length += nbytes; 471 | line->position += nbytes; 472 | 473 | return EDIT_INSERT; 474 | } 475 | 476 | static edit_status 477 | editor_enter(rp_input_line *line) 478 | { 479 | line->buffer[line->length] = '\0'; 480 | history_add(line->history_id, line->buffer); 481 | return EDIT_DONE; 482 | } 483 | 484 | static edit_status 485 | editor_paste_selection(rp_input_line *line) 486 | { 487 | char *text; 488 | 489 | text = get_selection(); 490 | if (text) { 491 | editor_insert(line, text); 492 | free(text); 493 | return EDIT_INSERT; 494 | } else 495 | return EDIT_NO_OP; 496 | } 497 | 498 | static edit_status 499 | editor_complete(rp_input_line *line, int direction) 500 | { 501 | char *tmp; 502 | char *s; 503 | 504 | /* 505 | * Create our partial string that will be used for completion. It is 506 | * the characters up to the position of the cursor. 507 | */ 508 | tmp = xmalloc(line->position + 1); 509 | memcpy(tmp, line->buffer, line->position); 510 | tmp[line->position] = '\0'; 511 | 512 | /* 513 | * We don't need to free s because it's a string from the completion 514 | * list. 515 | */ 516 | s = completions_complete(line->compl, tmp, direction); 517 | free(tmp); 518 | 519 | if (s == NULL) 520 | return EDIT_NO_OP; 521 | 522 | /* Insert the completion. */ 523 | backward_kill_line(line); 524 | editor_insert(line, s); 525 | 526 | return EDIT_COMPLETE; 527 | } 528 | 529 | static edit_status 530 | editor_complete_next(rp_input_line *line) 531 | { 532 | return editor_complete(line, COMPLETION_NEXT); 533 | } 534 | 535 | static edit_status 536 | editor_complete_prev(rp_input_line *line) 537 | { 538 | return editor_complete(line, COMPLETION_PREVIOUS); 539 | } 540 | -------------------------------------------------------------------------------- /editor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef _SDORFEHS_EDITOR_H 20 | #define _SDORFEHS_EDITOR_H 1 21 | 22 | #include "utf8.h" 23 | 24 | typedef enum edit_status { 25 | EDIT_INSERT, 26 | EDIT_DELETE, 27 | EDIT_MOVE, 28 | EDIT_COMPLETE, 29 | EDIT_ABORT, 30 | EDIT_DONE, 31 | EDIT_NO_OP 32 | } edit_status; 33 | 34 | /* Input line functions */ 35 | rp_input_line *input_line_new(char *prompt, char *preinput, int history_id, 36 | enum completion_styles style, completion_fn fn); 37 | void input_line_free(rp_input_line * line); 38 | 39 | edit_status execute_edit_action(rp_input_line *line, KeySym ch, 40 | unsigned int modifier, char *keysym_buf); 41 | 42 | #endif /* ! _SDORFEHS_EDITOR_H */ 43 | -------------------------------------------------------------------------------- /events.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Function prototypes 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_EVENTS_H 21 | #define _SDORFEHS_EVENTS_H 1 22 | 23 | void listen_for_events(void); 24 | void show_rudeness_msg(rp_window *win, int raised); 25 | 26 | #endif /* _SDORFEHS_EVENTS_H */ 27 | -------------------------------------------------------------------------------- /format.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcs/sdorfehs/ee2a18f2659a1f8b6d626893e38926618d7bfbe7/format.c -------------------------------------------------------------------------------- /format.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcs/sdorfehs/ee2a18f2659a1f8b6d626893e38926618d7bfbe7/format.h -------------------------------------------------------------------------------- /frame.c: -------------------------------------------------------------------------------- 1 | /* 2 | * functions that manipulate the frame structure. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #include "sdorfehs.h" 21 | 22 | #include 23 | #include 24 | 25 | int 26 | frame_left(rp_frame *frame) 27 | { 28 | return frame->x; 29 | } 30 | 31 | int 32 | frame_left_screen_edge(rp_frame *frame) 33 | { 34 | return (frame_left(frame) <= screen_left(frame->vscreen->screen) + 35 | defaults.padding_left); 36 | } 37 | 38 | int 39 | frame_top(rp_frame *frame) 40 | { 41 | return frame->y; 42 | } 43 | 44 | int 45 | frame_top_screen_edge(rp_frame *frame) 46 | { 47 | return (frame_top(frame) <= (screen_top(frame->vscreen->screen) + 48 | defaults.padding_top)); 49 | } 50 | 51 | int 52 | frame_right(rp_frame *frame) 53 | { 54 | return frame->x + frame->width; 55 | } 56 | 57 | int 58 | frame_right_screen_edge(rp_frame *frame) 59 | { 60 | return (frame_right(frame) >= screen_right(frame->vscreen->screen) - 61 | defaults.padding_right); 62 | } 63 | 64 | int 65 | frame_bottom(rp_frame *frame) 66 | { 67 | return frame->y + frame->height; 68 | } 69 | 70 | int 71 | frame_bottom_screen_edge(rp_frame *frame) 72 | { 73 | return (frame_bottom(frame) >= screen_bottom(frame->vscreen->screen) - 74 | defaults.padding_bottom); 75 | } 76 | 77 | int 78 | frame_width(rp_frame *frame) 79 | { 80 | return frame->width; 81 | } 82 | 83 | int 84 | frame_height(rp_frame *frame) 85 | { 86 | return frame->height; 87 | } 88 | 89 | void 90 | frame_resize_left(rp_frame *frame, int amount) 91 | { 92 | frame->x -= amount; 93 | frame->width += amount; 94 | } 95 | 96 | void 97 | frame_resize_right(rp_frame *frame, int amount) 98 | { 99 | frame->width += amount; 100 | } 101 | 102 | void 103 | frame_resize_up(rp_frame *frame, int amount) 104 | { 105 | frame->y -= amount; 106 | frame->height += amount; 107 | } 108 | 109 | void 110 | frame_resize_down(rp_frame *frame, int amount) 111 | { 112 | frame->height += amount; 113 | } 114 | 115 | void 116 | mark_edge_frames(void) 117 | { 118 | rp_screen *s; 119 | rp_vscreen *v; 120 | rp_frame *f; 121 | 122 | list_for_each_entry(s, &rp_screens, node) { 123 | list_for_each_entry(v, &s->vscreens, node) { 124 | list_for_each_entry(f, &v->frames, node) { 125 | f->edges = 0; 126 | 127 | if (frame_top_screen_edge(f)) 128 | f->edges |= EDGE_TOP; 129 | if (frame_left_screen_edge(f)) 130 | f->edges |= EDGE_LEFT; 131 | if (frame_right_screen_edge(f)) 132 | f->edges |= EDGE_RIGHT; 133 | if (frame_bottom_screen_edge(f)) 134 | f->edges |= EDGE_BOTTOM; 135 | } 136 | } 137 | } 138 | } 139 | 140 | static void 141 | init_frame(rp_frame *f) 142 | { 143 | f->number = 0; 144 | f->x = 0; 145 | f->y = 0; 146 | f->width = 0; 147 | f->height = 0; 148 | f->win_number = 0; 149 | f->last_access = 0; 150 | f->dedicated = 0; 151 | f->restore_win_number = EMPTY; 152 | } 153 | 154 | rp_frame * 155 | frame_new(rp_vscreen *v) 156 | { 157 | rp_frame *f; 158 | 159 | f = xmalloc(sizeof(rp_frame)); 160 | init_frame(f); 161 | f->vscreen = v; 162 | f->number = numset_request(v->frames_numset); 163 | 164 | return f; 165 | } 166 | 167 | void 168 | frame_free(rp_vscreen *v, rp_frame *f) 169 | { 170 | numset_release(v->frames_numset, f->number); 171 | free(f); 172 | } 173 | 174 | rp_frame * 175 | frame_copy(rp_frame *frame) 176 | { 177 | rp_frame *copy; 178 | 179 | copy = xmalloc(sizeof(rp_frame)); 180 | 181 | copy->vscreen = frame->vscreen; 182 | copy->number = frame->number; 183 | copy->x = frame->x; 184 | copy->y = frame->y; 185 | copy->width = frame->width; 186 | copy->height = frame->height; 187 | copy->win_number = frame->win_number; 188 | copy->last_access = frame->last_access; 189 | 190 | return copy; 191 | } 192 | 193 | char * 194 | frame_dump(rp_frame *frame, rp_vscreen *vscreen) 195 | { 196 | rp_window *win; 197 | char *tmp; 198 | struct sbuf *s; 199 | 200 | /* rather than use win_number, use the X11 window ID. */ 201 | win = find_window_number(frame->win_number); 202 | 203 | s = sbuf_new(0); 204 | sbuf_printf(s, "(frame :number %d :x %d :y %d :width %d :height %d " 205 | ":screenw %d :screenh %d :window %ld :last-access %d :dedicated %d)", 206 | frame->number, 207 | frame->x, 208 | frame->y, 209 | frame->width, 210 | frame->height, 211 | vscreen->screen->width, 212 | vscreen->screen->height, 213 | win ? win->w : 0, 214 | frame->last_access, 215 | frame->dedicated); 216 | 217 | /* Extract the string and return it, and don't forget to free s. */ 218 | tmp = sbuf_get(s); 219 | free(s); 220 | return tmp; 221 | } 222 | 223 | /* Used only by frame_read */ 224 | #define read_slot(x) do { tmp = strtok_ws (NULL); x = strtol(tmp,NULL,10); } while(0) 225 | 226 | rp_frame * 227 | frame_read(char *str, rp_vscreen *vscreen) 228 | { 229 | Window w = 0L; 230 | rp_window *win; 231 | rp_frame *f; 232 | char *tmp, *d; 233 | int s_width = -1; 234 | int s_height = -1; 235 | 236 | /* Create a blank frame. */ 237 | f = xmalloc(sizeof(rp_frame)); 238 | init_frame(f); 239 | f->vscreen = vscreen; 240 | 241 | PRINT_DEBUG(("parsing '%s'\n", str)); 242 | 243 | d = xstrdup(str); 244 | tmp = strtok_ws(d); 245 | 246 | /* Verify it starts with '(frame ' */ 247 | if (tmp == NULL || strcmp(tmp, "(frame")) { 248 | PRINT_DEBUG(("Doesn't start with '(frame '\n")); 249 | free(d); 250 | free(f); 251 | return NULL; 252 | } 253 | /* NOTE: there is no check to make sure each field was filled in. */ 254 | tmp = strtok_ws(NULL); 255 | while (tmp) { 256 | if (!strcmp(tmp, ":number")) 257 | read_slot(f->number); 258 | else if (!strcmp(tmp, ":x")) 259 | read_slot(f->x); 260 | else if (!strcmp(tmp, ":y")) 261 | read_slot(f->y); 262 | else if (!strcmp(tmp, ":width")) 263 | read_slot(f->width); 264 | else if (!strcmp(tmp, ":height")) 265 | read_slot(f->height); 266 | else if (!strcmp(tmp, ":screenw")) 267 | read_slot(s_width); 268 | else if (!strcmp(tmp, ":screenh")) 269 | read_slot(s_height); 270 | else if (!strcmp(tmp, ":window")) 271 | read_slot(w); 272 | else if (!strcmp(tmp, ":last-access")) 273 | read_slot(f->last_access); 274 | else if (!strcmp(tmp, ":dedicated")) { 275 | /* f->dedicated is unsigned, so read into local variable. */ 276 | long dedicated; 277 | 278 | read_slot(dedicated); 279 | if (dedicated <= 0) 280 | f->dedicated = 0; 281 | else 282 | f->dedicated = 1; 283 | } else if (!strcmp(tmp, ")")) 284 | break; 285 | else 286 | warn("unknown slot reading frame: %s", tmp); 287 | 288 | /* Read the next token. */ 289 | tmp = strtok_ws(NULL); 290 | } 291 | if (tmp) 292 | warnx("frame has trailing garbage: %s", tmp); 293 | free(d); 294 | 295 | /* adjust x, y, width and height to a possible screen size change */ 296 | if (s_width > 0) { 297 | f->x = (f->x * vscreen->screen->width) / s_width; 298 | f->width = (f->width * vscreen->screen->width) / s_width; 299 | } 300 | if (s_height > 0) { 301 | f->y = (f->y * vscreen->screen->height) / s_height; 302 | f->height = (f->height * vscreen->screen->height) / s_height; 303 | } 304 | /* 305 | * Perform some integrity checks on what we got and fix any problems. 306 | */ 307 | if (f->number <= 0) 308 | f->number = 0; 309 | if (f->x <= 0) 310 | f->x = 0; 311 | if (f->y <= 0) 312 | f->y = 0; 313 | if (f->width <= (defaults.window_border_width * 2) + (defaults.gap * 2)) 314 | f->width = (defaults.window_border_width * 2) + 315 | (defaults.gap * 2) + 1; 316 | if (f->height <= (defaults.window_border_width * 2) + 317 | (defaults.gap * 2)) 318 | f->height = (defaults.window_border_width * 2) + 319 | (defaults.gap * 2) + 1; 320 | if (f->last_access < 0) 321 | f->last_access = 0; 322 | 323 | /* Find the window with the X11 window ID. */ 324 | win = find_window_in_list(w, &rp_mapped_window); 325 | if (win) 326 | f->win_number = win->number; 327 | else 328 | f->win_number = EMPTY; 329 | 330 | return f; 331 | } 332 | 333 | #undef read_slot 334 | -------------------------------------------------------------------------------- /frame.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef FRAME_H 20 | #define FRAME_H 21 | 22 | void frame_resize_down(rp_frame *frame, int amount); 23 | void frame_resize_up(rp_frame *frame, int amount); 24 | void frame_resize_right(rp_frame *frame, int amount); 25 | void frame_resize_left(rp_frame *frame, int amount); 26 | void mark_edge_frames(void); 27 | int frame_height(rp_frame *frame); 28 | int frame_width(rp_frame *frame); 29 | int frame_bottom(rp_frame *frame); 30 | int frame_bottom_screen_edge(rp_frame *frame); 31 | int frame_right(rp_frame *frame); 32 | int frame_right_screen_edge(rp_frame *frame); 33 | int frame_top(rp_frame *frame); 34 | int frame_top_screen_edge(rp_frame *frame); 35 | int frame_left(rp_frame *frame); 36 | int frame_left_screen_edge(rp_frame *frame); 37 | int frame_bottom_abs(rp_frame *frame); 38 | int frame_right_abs(rp_frame *frame); 39 | int frame_top_abs(rp_frame *frame); 40 | int frame_left_abs(rp_frame *frame); 41 | 42 | rp_frame *frame_new(rp_vscreen *v); 43 | void frame_free(rp_vscreen *v, rp_frame *f); 44 | rp_frame *frame_copy(rp_frame *frame); 45 | char *frame_dump(rp_frame *frame, rp_vscreen *vscreen); 46 | rp_frame *frame_read(char *str, rp_vscreen *vscreen); 47 | 48 | rp_vscreen *frames_vscreen(rp_frame *); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /globals.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #include "sdorfehs.h" 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /* 33 | * Several systems seem not to have WAIT_ANY defined, so define it if it isn't. 34 | */ 35 | #ifndef WAIT_ANY 36 | #define WAIT_ANY -1 37 | #endif 38 | 39 | /* Some systems don't define the close-on-exec flag in fcntl.h */ 40 | #ifndef FD_CLOEXEC 41 | #define FD_CLOEXEC 1 42 | #endif 43 | 44 | int alarm_signalled = 0; 45 | int kill_signalled = 0; 46 | int hup_signalled = 0; 47 | int chld_signalled = 0; 48 | 49 | int rp_font_ascent, rp_font_descent, rp_font_width; 50 | 51 | Atom wm_name; 52 | Atom wm_state; 53 | Atom wm_change_state; 54 | Atom wm_protocols; 55 | Atom wm_delete; 56 | Atom wm_take_focus; 57 | Atom wm_colormaps; 58 | 59 | Atom rp_selection; 60 | 61 | /* TEXT atoms */ 62 | Atom xa_string; 63 | Atom xa_compound_text; 64 | Atom xa_utf8_string; 65 | 66 | /* netwm atoms */ 67 | Atom _net_active_window; 68 | Atom _net_client_list; 69 | Atom _net_client_list_stacking; 70 | Atom _net_current_desktop; 71 | Atom _net_number_of_desktops; 72 | Atom _net_supported; 73 | Atom _net_workarea; 74 | Atom _net_wm_name; 75 | Atom _net_wm_pid; 76 | Atom _net_wm_state; 77 | Atom _net_wm_state_fullscreen; 78 | Atom _net_wm_window_type; 79 | Atom _net_wm_window_type_dialog; 80 | Atom _net_wm_window_type_dock; 81 | Atom _net_wm_window_type_splash; 82 | Atom _net_wm_window_type_tooltip; 83 | Atom _net_wm_window_type_utility; 84 | Atom _net_supporting_wm_check; 85 | 86 | LIST_HEAD(rp_screens); 87 | rp_screen *rp_current_screen; 88 | rp_global_screen rp_glob_screen; 89 | 90 | Display *dpy; 91 | 92 | int rp_have_xrandr; 93 | 94 | LIST_HEAD(rp_children); 95 | struct rp_defaults defaults; 96 | 97 | int ignore_badwindow = 0; 98 | 99 | char **myargv; 100 | 101 | struct rp_key prefix_key; 102 | 103 | struct modifier_info rp_modifier_info; 104 | 105 | /* rudeness levels */ 106 | int rp_honour_transient_raise = 1; 107 | int rp_honour_normal_raise = 1; 108 | int rp_honour_transient_map = 1; 109 | int rp_honour_normal_map = 1; 110 | int rp_honour_vscreen_switch = 0; 111 | 112 | char *rp_error_msg = NULL; 113 | 114 | /* Global frame numset */ 115 | struct numset *rp_frame_numset; 116 | 117 | /* The X11 selection globals */ 118 | rp_xselection selection; 119 | 120 | static void 121 | x_export_selection(void) 122 | { 123 | rp_screen *screen; 124 | 125 | list_first(screen, &rp_screens, node); 126 | if (!screen) 127 | return; 128 | 129 | /* Hang the selections off screen 0's key window. */ 130 | XSetSelectionOwner(dpy, XA_PRIMARY, screen->key_window, CurrentTime); 131 | if (XGetSelectionOwner(dpy, XA_PRIMARY) != screen->key_window) 132 | warnx("can't get primary selection"); 133 | XChangeProperty(dpy, screen->root, XA_CUT_BUFFER0, xa_string, 8, 134 | PropModeReplace, (unsigned char *) selection.text, selection.len); 135 | } 136 | 137 | void 138 | set_nselection(char *txt, int len) 139 | { 140 | int i; 141 | 142 | /* Update the selection structure */ 143 | free(selection.text); 144 | 145 | /* Copy the string by hand. */ 146 | selection.text = xmalloc(len + 1); 147 | selection.len = len + 1; 148 | for (i = 0; i < len; i++) 149 | selection.text[i] = txt[i]; 150 | selection.text[len] = 0; 151 | 152 | x_export_selection(); 153 | } 154 | 155 | void 156 | set_selection(char *txt) 157 | { 158 | /* Update the selection structure */ 159 | free(selection.text); 160 | selection.text = xstrdup(txt); 161 | selection.len = strlen(txt); 162 | 163 | x_export_selection(); 164 | } 165 | 166 | static char * 167 | get_cut_buffer(void) 168 | { 169 | int nbytes; 170 | char *data; 171 | 172 | PRINT_DEBUG(("trying the cut buffer\n")); 173 | 174 | data = XFetchBytes(dpy, &nbytes); 175 | 176 | if (data) { 177 | struct sbuf *s = sbuf_new(0); 178 | sbuf_nconcat(s, data, nbytes); 179 | XFree(data); 180 | return sbuf_free_struct(s); 181 | } else 182 | return NULL; 183 | } 184 | 185 | /* Lifted the code from rxvt. */ 186 | static char * 187 | get_primary_selection(void) 188 | { 189 | long nread; 190 | unsigned long bytes_after; 191 | XTextProperty ct; 192 | struct sbuf *s = sbuf_new(0); 193 | 194 | for (nread = 0, bytes_after = 1; bytes_after > 0; nread += ct.nitems) { 195 | if ((XGetWindowProperty(dpy, rp_current_screen->input_window, 196 | rp_selection, (nread / 4), 4096, True, AnyPropertyType, 197 | &ct.encoding, &ct.format, &ct.nitems, &bytes_after, 198 | &ct.value) != Success)) { 199 | XFree(ct.value); 200 | sbuf_free(s); 201 | return NULL; 202 | } 203 | if (ct.value == NULL) 204 | continue; 205 | /* 206 | * Accumulate the data. FIXME: ct.value may not be NULL 207 | * terminated. 208 | */ 209 | sbuf_nconcat(s, (const char *) ct.value, ct.nitems); 210 | XFree(ct.value); 211 | } 212 | return sbuf_free_struct(s); 213 | } 214 | 215 | char * 216 | get_selection(void) 217 | { 218 | Atom property; 219 | XEvent ev; 220 | rp_screen *s = rp_current_screen; 221 | int loops = 1000; 222 | 223 | /* Just insert our text, if we own the selection. */ 224 | if (selection.text) { 225 | return xstrdup(selection.text); 226 | } else { 227 | /* be a good icccm citizen */ 228 | XDeleteProperty(dpy, s->input_window, rp_selection); 229 | /* 230 | * TODO: we shouldn't use CurrentTime here, use the time of the 231 | * XKeyEvent, should we fake it? 232 | */ 233 | XConvertSelection(dpy, XA_PRIMARY, xa_string, rp_selection, 234 | s->input_window, CurrentTime); 235 | 236 | /* This seems like a hack. */ 237 | while (!XCheckTypedWindowEvent(dpy, s->input_window, 238 | SelectionNotify, &ev)) { 239 | if (loops == 0) { 240 | warnx("selection request timed out"); 241 | return NULL; 242 | } 243 | usleep(10000); 244 | loops--; 245 | } 246 | 247 | PRINT_DEBUG(("SelectionNotify event\n")); 248 | 249 | property = ev.xselection.property; 250 | 251 | if (property != None) 252 | return get_primary_selection(); 253 | else 254 | return get_cut_buffer(); 255 | } 256 | } 257 | 258 | /* The hook dictionary globals. */ 259 | 260 | LIST_HEAD(rp_key_hook); 261 | LIST_HEAD(rp_switch_win_hook); 262 | LIST_HEAD(rp_switch_frame_hook); 263 | LIST_HEAD(rp_switch_screen_hook); 264 | LIST_HEAD(rp_switch_vscreen_hook); 265 | LIST_HEAD(rp_quit_hook); 266 | LIST_HEAD(rp_restart_hook); 267 | LIST_HEAD(rp_delete_window_hook); 268 | LIST_HEAD(rp_new_window_hook); 269 | LIST_HEAD(rp_title_changed_hook); 270 | 271 | struct rp_hook_db_entry rp_hook_db[] = 272 | {{"key", &rp_key_hook}, 273 | {"switchwin", &rp_switch_win_hook}, 274 | {"switchframe", &rp_switch_frame_hook}, 275 | {"switchscreen", &rp_switch_screen_hook}, 276 | {"switchvscreen", &rp_switch_vscreen_hook}, 277 | {"deletewindow", &rp_delete_window_hook}, 278 | {"quit", &rp_quit_hook}, 279 | {"restart", &rp_restart_hook}, 280 | {"newwindow", &rp_new_window_hook}, 281 | {"titlechanged", &rp_title_changed_hook}, 282 | {NULL, NULL} 283 | }; 284 | 285 | void 286 | set_rp_window_focus(rp_window *win) 287 | { 288 | PRINT_DEBUG(("Giving focus to '%s'\n", window_name(win))); 289 | XSetInputFocus(dpy, win->w, 290 | RevertToPointerRoot, CurrentTime); 291 | set_atom(win->vscreen->screen->root, _net_active_window, XA_WINDOW, 292 | &win->w, 1); 293 | } 294 | 295 | void 296 | set_window_focus(Window window) 297 | { 298 | PRINT_DEBUG(("Giving focus to %ld\n", window)); 299 | XSetInputFocus(dpy, window, 300 | RevertToPointerRoot, CurrentTime); 301 | } 302 | 303 | XftFont * 304 | rp_get_font(rp_screen *s, char *font) 305 | { 306 | XftFont *f; 307 | int fslots = sizeof(s->xft_font_cache) / sizeof(struct rp_font); 308 | int x; 309 | 310 | if (!font || font[0] == '\0') 311 | return s->xft_font; 312 | 313 | for (x = 0; x < fslots; x++) { 314 | if (!s->xft_font_cache[x].name) 315 | break; 316 | 317 | if (strcmp(s->xft_font_cache[x].name, font) == 0) 318 | return s->xft_font_cache[x].font; 319 | } 320 | 321 | /* not in the cache, make sure we can open it first */ 322 | f = XftFontOpenName(dpy, DefaultScreen(dpy), font); 323 | if (!f) { 324 | warnx("failed opening xft font \"%s\"", font); 325 | return s->xft_font; 326 | } 327 | 328 | PRINT_DEBUG(("font \"%s\" not in font cache\n", font)); 329 | 330 | /* free up the last slot if needed */ 331 | if (x == fslots) { 332 | free(s->xft_font_cache[x - 1].name); 333 | XftFontClose(dpy, s->xft_font_cache[x - 1].font); 334 | } 335 | 336 | /* shift all the cache entries to free up the first slot */ 337 | for (x = fslots - 1; x >= 1; x--) 338 | memcpy(&s->xft_font_cache[x], &s->xft_font_cache[x - 1], 339 | sizeof(struct rp_font)); 340 | 341 | s->xft_font_cache[0].name = xstrdup(font); 342 | s->xft_font_cache[0].font = f; 343 | 344 | return f; 345 | } 346 | 347 | void 348 | rp_clear_cached_fonts(rp_screen *s) 349 | { 350 | int x; 351 | 352 | for (x = 0; x < (sizeof(s->xft_font_cache) / sizeof(struct rp_font)); 353 | x++) { 354 | if (s->xft_font_cache[x].name) { 355 | free(s->xft_font_cache[x - 1].name); 356 | XftFontClose(dpy, s->xft_font_cache[x].font); 357 | } 358 | } 359 | } 360 | 361 | void 362 | rp_draw_string(rp_screen *s, Drawable d, int style, int x, int y, char *string, 363 | int length, char *font, char *color) 364 | { 365 | XftDraw *draw; 366 | XftColor xftcolor; 367 | XftFont *f = rp_get_font(s, font); 368 | 369 | if (length < 0) 370 | length = strlen(string); 371 | 372 | draw = XftDrawCreate(dpy, d, DefaultVisual(dpy, s->screen_num), 373 | DefaultColormap(dpy, s->screen_num)); 374 | if (!draw) { 375 | warnx("no Xft font available"); 376 | return; 377 | } 378 | 379 | if (color == NULL) { 380 | if (style == STYLE_NORMAL) 381 | memcpy(&xftcolor, &s->xft_fgcolor, sizeof(XftColor)); 382 | else 383 | memcpy(&xftcolor, &s->xft_bgcolor, sizeof(XftColor)); 384 | } else { 385 | /* 386 | * This won't actually allocate anything if the color is 387 | * already allocated. 388 | */ 389 | if (!XftColorAllocName(dpy, DefaultVisual(dpy, s->screen_num), 390 | DefaultColormap(dpy, s->screen_num), color, &xftcolor)) { 391 | warnx("couldn't XftColorAllocName \"%s\"", color); 392 | memcpy(&xftcolor, &s->xft_fgcolor, sizeof(XftColor)); 393 | } 394 | } 395 | 396 | XftDrawStringUtf8(draw, &xftcolor, f, x, y, (FcChar8 *)string, length); 397 | XftDrawDestroy(draw); 398 | } 399 | 400 | int 401 | rp_text_width(rp_screen *s, char *string, int count, char *font) 402 | { 403 | XGlyphInfo extents; 404 | XftFont *f = rp_get_font(s, font); 405 | 406 | if (count < 0) 407 | count = strlen(string); 408 | 409 | XftTextExtentsUtf8(dpy, f, (FcChar8 *)string, count, &extents); 410 | 411 | return extents.xOff; 412 | } 413 | 414 | /* A case insensitive strncmp. */ 415 | int 416 | str_comp(char *s1, char *s2, size_t len) 417 | { 418 | size_t i; 419 | 420 | for (i = 0; i < len; i++) 421 | if (toupper((unsigned char)s1[i]) != toupper((unsigned char)s2[i])) 422 | return 0; 423 | 424 | return 1; 425 | } 426 | 427 | /* 428 | * Check for child processes that have quit but haven't been acknowledged yet. 429 | * Update their structure. 430 | */ 431 | void 432 | check_child_procs(void) 433 | { 434 | rp_child_info *cur; 435 | int pid, status; 436 | while (1) { 437 | pid = waitpid(WAIT_ANY, &status, WNOHANG); 438 | if (pid <= 0) 439 | break; 440 | 441 | PRINT_DEBUG(("Child status: %d\n", WEXITSTATUS(status))); 442 | 443 | /* Find the child and update its structure. */ 444 | list_for_each_entry(cur, &rp_children, node) { 445 | if (cur->pid == pid) { 446 | cur->terminated = 1; 447 | cur->status = WEXITSTATUS(status); 448 | break; 449 | } 450 | } 451 | 452 | chld_signalled = 1; 453 | } 454 | } 455 | 456 | void 457 | chld_handler(int signum) 458 | { 459 | int serrno; 460 | 461 | serrno = errno; 462 | check_child_procs(); 463 | errno = serrno; 464 | } 465 | 466 | void 467 | set_sig_handler(int sig, void (*action)(int)) 468 | { 469 | struct sigaction act; 470 | 471 | memset(&act, 0, sizeof(act)); 472 | act.sa_handler = action; 473 | sigemptyset(&act.sa_mask); 474 | if (sigaction(sig, &act, NULL)) 475 | warnx("error setting signal handler"); 476 | } 477 | 478 | void 479 | set_close_on_exec(int fd) 480 | { 481 | int flags = fcntl(fd, F_GETFD); 482 | if (flags >= 0) 483 | fcntl(fd, F_SETFD, flags | FD_CLOEXEC); 484 | } 485 | 486 | void 487 | read_rc_file(FILE *file) 488 | { 489 | char *line; 490 | size_t linesize = 256; 491 | 492 | line = xmalloc(linesize); 493 | 494 | while (getline(&line, &linesize, file) != -1) { 495 | line[strcspn(line, "\n")] = '\0'; 496 | 497 | PRINT_DEBUG(("rcfile line: %s\n", line)); 498 | 499 | if (*line != '\0' && *line != '#') { 500 | cmdret *result; 501 | result = command(0, line); 502 | 503 | /* Gobble the result. */ 504 | if (result) 505 | cmdret_free(result); 506 | } 507 | } 508 | 509 | free(line); 510 | } 511 | 512 | const char * 513 | get_homedir(void) 514 | { 515 | char *homedir; 516 | 517 | homedir = getenv("HOME"); 518 | if (homedir != NULL && homedir[0] == '\0') 519 | homedir = NULL; 520 | 521 | if (homedir == NULL) { 522 | struct passwd *pw; 523 | 524 | pw = getpwuid(getuid()); 525 | if (pw != NULL) 526 | homedir = pw->pw_dir; 527 | 528 | if (homedir != NULL && homedir[0] == '\0') 529 | homedir = NULL; 530 | } 531 | 532 | return homedir; 533 | } 534 | 535 | char * 536 | get_config_dir(void) 537 | { 538 | DIR *d; 539 | const char *homedir; 540 | char *xdg_config, *home_config; 541 | int xdg_alloc = 0; 542 | 543 | homedir = get_homedir(); 544 | if (!homedir) 545 | errx(1, "no home directory"); 546 | 547 | xdg_config = getenv("XDG_CONFIG_HOME"); 548 | if (xdg_config == NULL || !strlen(xdg_config)) { 549 | xdg_config = xsprintf("%s/.config", homedir); 550 | xdg_alloc = 1; 551 | } 552 | 553 | if (!(d = opendir(xdg_config))) { 554 | if (mkdir(xdg_config, 0755) == -1) 555 | err(1, "failed creating %s", xdg_config); 556 | 557 | if (!(d = opendir(xdg_config))) 558 | err(1, "failed opening %s", xdg_config); 559 | } 560 | closedir(d); 561 | 562 | home_config = xsprintf("%s/sdorfehs", xdg_config); 563 | if (!(d = opendir(home_config))) { 564 | if (mkdir(home_config, 0755) == -1) 565 | err(1, "failed creating %s", home_config); 566 | 567 | if (!(d = opendir(home_config))) 568 | err(1, "failed opening %s", home_config); 569 | } 570 | closedir(d); 571 | 572 | if (xdg_alloc) 573 | free(xdg_config); 574 | 575 | return home_config; 576 | } 577 | 578 | void 579 | clean_up(void) 580 | { 581 | rp_screen *cur; 582 | rp_vscreen *vcur; 583 | struct list_head *iter, *tmp, *iter2, *tmp2; 584 | 585 | history_save(); 586 | 587 | free_keymaps(); 588 | free_aliases(); 589 | free_user_commands(); 590 | free_bar(); 591 | free_window_stuff(); 592 | 593 | list_for_each_safe_entry(cur, iter, tmp, &rp_screens, node) { 594 | list_for_each_safe_entry(vcur, iter2, tmp2, &cur->vscreens, node) 595 | vscreen_del(vcur); 596 | 597 | list_del(&cur->node); 598 | screen_free(cur); 599 | free(cur); 600 | } 601 | 602 | screen_free_final(); 603 | 604 | /* Delete the undo histories */ 605 | clear_frame_undos(); 606 | 607 | /* Free the global frame numset shared by all screens. */ 608 | numset_free(rp_frame_numset); 609 | 610 | free(defaults.window_fmt); 611 | 612 | XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 613 | XCloseDisplay(dpy); 614 | } 615 | 616 | void 617 | register_atom(Atom *a, char *name) 618 | { 619 | *a = XInternAtom(dpy, name, False); 620 | PRINT_DEBUG(("Registered Atom %ld = %s\n", (unsigned long)*a, name)); 621 | append_atom(DefaultRootWindow(dpy), _net_supported, XA_ATOM, a, 1); 622 | } 623 | 624 | int 625 | set_atom(Window w, Atom a, Atom type, unsigned long *val, unsigned long nitems) 626 | { 627 | return (XChangeProperty(dpy, w, a, type, 32, PropModeReplace, 628 | (unsigned char *)val, nitems) == Success); 629 | } 630 | 631 | int 632 | append_atom(Window w, Atom a, Atom type, unsigned long *val, 633 | unsigned long nitems) 634 | { 635 | return (XChangeProperty(dpy, w, a, type, 32, PropModeAppend, 636 | (unsigned char *)val, nitems) == Success); 637 | } 638 | 639 | unsigned long 640 | get_atom(Window w, Atom a, Atom type, unsigned long off, unsigned long *ret, 641 | unsigned long nitems, unsigned long *left) 642 | { 643 | Atom real_type; 644 | int i, real_format = 0; 645 | unsigned long items_read = 0; 646 | unsigned long bytes_left = 0; 647 | unsigned long *p; 648 | unsigned char *data; 649 | 650 | XGetWindowProperty(dpy, w, a, off, nitems, False, type, &real_type, 651 | &real_format, &items_read, &bytes_left, &data); 652 | 653 | if (real_format == 32 && items_read) { 654 | p = (unsigned long *)data; 655 | for (i = 0; i < items_read; i++) 656 | *ret++ = *p++; 657 | XFree(data); 658 | if (left) 659 | *left = bytes_left; 660 | return items_read; 661 | } 662 | 663 | return 0; 664 | } 665 | 666 | void 667 | remove_atom(Window w, Atom a, Atom type, unsigned long remove) 668 | { 669 | unsigned long tmp, read, left, *new; 670 | int i, j = 0; 671 | 672 | read = get_atom(w, a, type, 0, &tmp, 1, &left); 673 | if (!read) 674 | return; 675 | 676 | new = malloc((read + left) * sizeof(*new)); 677 | if (read && tmp != remove) 678 | new[j++] = tmp; 679 | 680 | for (i = 1, read = left = 1; read && left; i += read) { 681 | read = get_atom(w, a, type, i, &tmp, 1, &left); 682 | if (!read) 683 | break; 684 | if (tmp != remove) 685 | new[j++] = tmp; 686 | } 687 | 688 | if (j) 689 | XChangeProperty(dpy, w, a, type, 32, PropModeReplace, 690 | (unsigned char *)new, j); 691 | else 692 | XDeleteProperty(dpy, w, a); 693 | 694 | free(new); 695 | } 696 | -------------------------------------------------------------------------------- /globals.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef GLOBALS_H 20 | #define GLOBALS_H 21 | 22 | #include "data.h" 23 | 24 | /* codes used in the cmdret code in actions.c */ 25 | #define RET_SUCCESS 1 26 | #define RET_FAILURE 0 27 | 28 | #define FONT_HEIGHT(s) ((s)->xft_font->ascent + (s)->xft_font->descent) 29 | #define FONT_ASCENT(s) ((s)->xft_font->ascent) 30 | #define FONT_DESCENT(s) ((s)->xft_font->descent) 31 | 32 | #define MAX_FONT_WIDTH(f) (rp_font_width) 33 | 34 | #define WIN_EVENTS (StructureNotifyMask | PropertyChangeMask | \ 35 | ColormapChangeMask | FocusChangeMask) 36 | 37 | /* 38 | * EMPTY is used when a frame doesn't contain a window, or a window doesn't 39 | * have a frame. Any time a field refers to the number of a 40 | * window/frame/screen/etc, Use EMPTY to denote a lack there of. 41 | */ 42 | #define EMPTY -1 43 | 44 | /* Possible values for defaults.window_list_style */ 45 | #define STYLE_ROW 0 46 | #define STYLE_COLUMN 1 47 | 48 | /* Possible values for defaults.win_name */ 49 | #define WIN_NAME_TITLE 0 50 | #define WIN_NAME_RES_CLASS 1 51 | #define WIN_NAME_RES_NAME 2 52 | 53 | /* Possible directions to traverse the completions list. */ 54 | #define COMPLETION_NEXT 0 55 | #define COMPLETION_PREVIOUS 1 56 | 57 | /* Font styles */ 58 | #define STYLE_NORMAL 0 59 | #define STYLE_INVERSE 1 60 | 61 | /* Whether or not we support xrandr */ 62 | extern int rp_have_xrandr; 63 | 64 | /* 65 | * Each child process is stored in this list. spawn, creates a new entry in 66 | * this list, the SIGCHLD handler sets child.terminated to be true and 67 | * handle_signals in events.c processes each terminated process by printing a 68 | * message saying the process ended and displaying it's exit code. 69 | */ 70 | extern struct list_head rp_children; 71 | 72 | extern struct rp_defaults defaults; 73 | 74 | /* Cached font info. */ 75 | extern int rp_font_ascent, rp_font_descent, rp_font_width; 76 | 77 | /* The prefix key also known as the command character under screen. */ 78 | extern struct rp_key prefix_key; 79 | 80 | /* 81 | * A list of mapped windows. These windows show up in the window list and have 82 | * a number assigned to them. 83 | */ 84 | extern struct list_head rp_mapped_window; 85 | 86 | /* 87 | * A list of unmapped windows. These windows do not have a number assigned to 88 | * them and are not visible/active. 89 | */ 90 | extern struct list_head rp_unmapped_window; 91 | 92 | /* The list of screens. */ 93 | extern struct list_head rp_screens; 94 | extern rp_screen *rp_current_screen; 95 | extern rp_global_screen rp_glob_screen; 96 | 97 | extern Display *dpy; 98 | 99 | extern XEvent rp_current_event; 100 | 101 | extern Atom rp_selection; 102 | 103 | extern Atom wm_name; 104 | extern Atom wm_state; 105 | extern Atom wm_change_state; 106 | extern Atom wm_protocols; 107 | extern Atom wm_delete; 108 | extern Atom wm_take_focus; 109 | extern Atom wm_colormaps; 110 | 111 | /* TEXT atoms */ 112 | extern Atom xa_string; 113 | extern Atom xa_compound_text; 114 | extern Atom xa_utf8_string; 115 | 116 | /* netwm atoms. */ 117 | extern Atom _net_active_window; 118 | extern Atom _net_client_list; 119 | extern Atom _net_client_list_stacking; 120 | extern Atom _net_current_desktop; 121 | extern Atom _net_number_of_desktops; 122 | extern Atom _net_supported; 123 | extern Atom _net_workarea; 124 | extern Atom _net_wm_name; 125 | extern Atom _net_wm_pid; 126 | extern Atom _net_wm_state; 127 | #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ 128 | #define _NET_WM_STATE_ADD 1 /* add/set property */ 129 | #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ 130 | extern Atom _net_wm_state_fullscreen; 131 | extern Atom _net_wm_window_type; 132 | extern Atom _net_wm_window_type_dialog; 133 | extern Atom _net_wm_window_type_dock; 134 | extern Atom _net_wm_window_type_splash; 135 | extern Atom _net_wm_window_type_tooltip; 136 | extern Atom _net_wm_window_type_utility; 137 | extern Atom _net_supporting_wm_check; 138 | 139 | /* 140 | * When unmapping or deleting windows, it is sometimes helpful to ignore a bad 141 | * window when attempting to clean the window up. This does just that when set 142 | * to 1 143 | */ 144 | extern int ignore_badwindow; 145 | 146 | /* Arguments passed at startup. */ 147 | extern char **myargv; 148 | 149 | /* Keeps track of which mod mask each modifier is under. */ 150 | extern struct modifier_info rp_modifier_info; 151 | 152 | /* 153 | * nonzero if an alarm signal was raised. This means we should hide our 154 | * popup windows. 155 | */ 156 | extern int alarm_signalled; 157 | extern int kill_signalled; 158 | extern int hup_signalled; 159 | extern int chld_signalled; 160 | 161 | /* rudeness levels */ 162 | extern int rp_honour_transient_raise; 163 | extern int rp_honour_normal_raise; 164 | extern int rp_honour_transient_map; 165 | extern int rp_honour_normal_map; 166 | extern int rp_honour_vscreen_switch; 167 | 168 | /* Keep track of X11 error messages. */ 169 | extern char *rp_error_msg; 170 | 171 | /* Number sets for windows. */ 172 | extern struct numset *rp_window_numset; 173 | 174 | extern struct list_head rp_key_hook; 175 | extern struct list_head rp_switch_win_hook; 176 | extern struct list_head rp_switch_frame_hook; 177 | extern struct list_head rp_switch_screen_hook; 178 | extern struct list_head rp_switch_vscreen_hook; 179 | extern struct list_head rp_delete_window_hook; 180 | extern struct list_head rp_quit_hook; 181 | extern struct list_head rp_restart_hook; 182 | extern struct list_head rp_new_window_hook; 183 | extern struct list_head rp_title_changed_hook; 184 | 185 | extern struct rp_hook_db_entry rp_hook_db[]; 186 | 187 | void set_rp_window_focus(rp_window * win); 188 | void set_window_focus(Window window); 189 | 190 | extern struct numset *rp_frame_numset; 191 | 192 | /* Selection handling globals */ 193 | extern rp_xselection selection; 194 | void set_selection(char *txt); 195 | void set_nselection(char *txt, int len); 196 | char *get_selection(void); 197 | 198 | /* Wrapper font functions to support Xft */ 199 | 200 | XftFont *rp_get_font(rp_screen *s, char *font); 201 | void rp_clear_cached_fonts(rp_screen *s); 202 | void rp_draw_string(rp_screen *s, Drawable d, int style, int x, int y, 203 | char *string, int length, char *font, char *color); 204 | int rp_text_width(rp_screen *s, char *string, int count, char *font); 205 | 206 | void check_child_procs(void); 207 | void chld_handler(int signum); 208 | void set_sig_handler(int sig, void (*action)(int)); 209 | void set_close_on_exec(int fd); 210 | void read_rc_file(FILE *file); 211 | const char *get_homedir(void); 212 | char *get_config_dir(void); 213 | void clean_up(void); 214 | 215 | void register_atom(Atom *a, char *name); 216 | int set_atom(Window w, Atom a, Atom type, unsigned long *val, 217 | unsigned long nitems); 218 | int append_atom(Window w, Atom a, Atom type, unsigned long *val, 219 | unsigned long nitems); 220 | unsigned long get_atom(Window w, Atom a, Atom type, unsigned long off, 221 | unsigned long *ret, unsigned long nitems, unsigned long *left); 222 | void remove_atom(Window w, Atom a, Atom type, unsigned long remove); 223 | 224 | #endif 225 | -------------------------------------------------------------------------------- /history.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "sdorfehs.h" 27 | 28 | static char * 29 | get_history_filename(void) 30 | { 31 | const char *homedir; 32 | char *filename; 33 | 34 | homedir = get_homedir(); 35 | 36 | if (homedir) { 37 | struct sbuf *buf; 38 | 39 | buf = sbuf_new(0); 40 | sbuf_printf(buf, "%s/.config/sdorfehs/%s", homedir, 41 | HISTORY_FILE); 42 | filename = sbuf_free_struct(buf); 43 | } else { 44 | filename = xstrdup(HISTORY_FILE); 45 | } 46 | 47 | return filename; 48 | } 49 | 50 | static const char * 51 | extract_shell_part(const char *p) 52 | { 53 | if (strncmp(p, "exec", 4) && strncmp(p, "verbexec", 8)) 54 | return NULL; 55 | while (*p && !isspace((unsigned char) *p)) 56 | p++; 57 | while (*p && isspace((unsigned char) *p)) 58 | p++; 59 | if (*p) 60 | return p; 61 | return NULL; 62 | } 63 | 64 | struct history_item { 65 | struct list_head node; 66 | char *line; 67 | }; 68 | 69 | static struct history { 70 | struct list_head head, *current; 71 | size_t count; 72 | } histories[hist_COUNT]; 73 | 74 | static void 75 | history_add_upto(int history_id, const char *item, size_t max) 76 | { 77 | struct history *h = histories + history_id; 78 | struct history_item *i; 79 | 80 | if (item == NULL || *item == '\0' || isspace((unsigned char) *item)) 81 | return; 82 | 83 | list_last(i, &histories[history_id].head, node); 84 | if (i && !strcmp(i->line, item)) 85 | return; 86 | 87 | if (history_id == hist_COMMAND) { 88 | const char *p = extract_shell_part(item); 89 | if (p) 90 | history_add_upto(hist_SHELLCMD, p, max); 91 | } 92 | while (h->count >= max) { 93 | list_first(i, &h->head, node); 94 | if (!i) { 95 | h->count = 0; 96 | break; 97 | } 98 | list_del(&i->node); 99 | free(i->line); 100 | free(i); 101 | h->count--; 102 | } 103 | 104 | if (max == 0) 105 | return; 106 | 107 | i = xmalloc(sizeof(*i)); 108 | i->line = xstrdup(item); 109 | 110 | list_add_tail(&i->node, &h->head); 111 | h->count++; 112 | } 113 | 114 | void 115 | history_add(int history_id, const char *item) 116 | { 117 | history_add_upto(history_id, item, defaults.history_size); 118 | } 119 | 120 | void 121 | history_load(void) 122 | { 123 | char *filename; 124 | FILE *f; 125 | char *line = NULL; 126 | size_t s = 0; 127 | ssize_t linelen; 128 | int id; 129 | 130 | for (id = hist_NONE; id < hist_COUNT; id++) { 131 | INIT_LIST_HEAD(&histories[id].head); 132 | histories[id].current = &histories[id].head; 133 | histories[id].count = 0; 134 | } 135 | 136 | filename = get_history_filename(); 137 | if (!filename) 138 | return; 139 | 140 | f = fopen(filename, "r"); 141 | if (!f) { 142 | PRINT_DEBUG(("could not load history from %s: %s", filename, 143 | strerror(errno))); 144 | free(filename); 145 | return; 146 | } 147 | while ((linelen = getline(&line, &s, f)) >= 0) { 148 | while (linelen > 0 && (line[linelen - 1] == '\n' || 149 | line[linelen - 1] == '\r')) { 150 | line[--linelen] = '\0'; 151 | } 152 | if (linelen == 0) 153 | continue; 154 | /* defaults.history_size might be only set later */ 155 | history_add_upto(hist_COMMAND, line, INT_MAX); 156 | } 157 | free(line); 158 | if (ferror(f)) { 159 | PRINT_DEBUG(("error reading history %s: %s\n", filename, 160 | strerror(errno))); 161 | fclose(f); 162 | free(filename); 163 | return; 164 | } 165 | if (fclose(f)) 166 | PRINT_DEBUG(("error reading %s: %s\n", filename, 167 | strerror(errno))); 168 | free(filename); 169 | } 170 | 171 | void 172 | history_save(void) 173 | { 174 | char *filename; 175 | FILE *f; 176 | struct history_item *item; 177 | 178 | if (!defaults.history_size) 179 | return; 180 | 181 | filename = get_history_filename(); 182 | if (!filename) 183 | return; 184 | 185 | f = fopen(filename, "w"); 186 | if (!f) { 187 | PRINT_DEBUG(("could not write history to %s: %s\n", filename, 188 | strerror(errno))); 189 | free(filename); 190 | return; 191 | } 192 | if (fchmod(fileno(f), 0600) == -1) 193 | warn("could not change mode to 0600 on %s", filename); 194 | 195 | list_for_each_entry(item, &histories[hist_COMMAND].head, node) { 196 | fputs(item->line, f); 197 | putc('\n', f); 198 | } 199 | 200 | if (ferror(f)) { 201 | PRINT_DEBUG(("error writing history to %s: %s\n", filename, 202 | strerror(errno))); 203 | fclose(f); 204 | free(filename); 205 | return; 206 | } 207 | if (fclose(f)) 208 | PRINT_DEBUG(("error writing history to %s: %s\n", filename, 209 | strerror(errno))); 210 | free(filename); 211 | } 212 | 213 | void 214 | history_reset(void) 215 | { 216 | int id; 217 | 218 | for (id = hist_NONE; id < hist_COUNT; id++) 219 | histories[id].current = &histories[id].head; 220 | } 221 | 222 | const char * 223 | history_previous(int history_id) 224 | { 225 | if (history_id == hist_NONE) 226 | return NULL; 227 | /* return NULL, if list empty or already at first */ 228 | if (histories[history_id].current == histories[history_id].head.next) 229 | return NULL; 230 | histories[history_id].current = histories[history_id].current->prev; 231 | return list_entry(histories[history_id].current, 232 | struct history_item, node)->line; 233 | } 234 | 235 | const char * 236 | history_next(int history_id) 237 | { 238 | if (history_id == hist_NONE) 239 | return NULL; 240 | /* return NULL, if list empty or already behind last */ 241 | if (histories[history_id].current == &histories[history_id].head) 242 | return NULL; 243 | histories[history_id].current = histories[history_id].current->next; 244 | if (histories[history_id].current == &histories[history_id].head) 245 | return NULL; 246 | return list_entry(histories[history_id].current, struct history_item, 247 | node)->line; 248 | } 249 | -------------------------------------------------------------------------------- /history.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef _SDORFEHS_HISTORY_H 20 | #define _SDORFEHS_HISTORY_H 1 21 | 22 | enum { 23 | hist_NONE = 0, hist_COMMAND, hist_SHELLCMD, 24 | hist_SELECT, hist_KEYMAP, hist_KEY, 25 | hist_WINDOW, hist_GRAVITY, hist_VSCREEN, 26 | hist_HOOK, hist_VARIABLE, hist_PROMPT, 27 | hist_OTHER, 28 | /* must be last, do not use, for length only: */ 29 | hist_COUNT 30 | }; 31 | 32 | void history_load(void); 33 | void history_save(void); 34 | void history_reset(void); 35 | void history_add(int, const char *item); 36 | const char *history_next(int); 37 | const char *history_previous(int); 38 | int history_expand_line(int, char *string, char **output); 39 | 40 | #endif /* ! _SDORFEHS_HISTORY_H */ 41 | -------------------------------------------------------------------------------- /hook.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | /* 20 | * A hook is simply a list of strings that get passed to command() in sequence. 21 | */ 22 | 23 | #include "sdorfehs.h" 24 | 25 | #include 26 | 27 | void 28 | hook_add(struct list_head *hook, struct sbuf *s) 29 | { 30 | struct sbuf *cur; 31 | 32 | /* Check if it's in the list already. */ 33 | list_for_each_entry(cur, hook, node) { 34 | if (!strcmp(sbuf_get(cur), sbuf_get(s))) { 35 | sbuf_free(s); 36 | return; 37 | } 38 | } 39 | 40 | /* It's not in the list, so add it. */ 41 | list_add_tail(&s->node, hook); 42 | } 43 | 44 | void 45 | hook_remove(struct list_head *hook, struct sbuf *s) 46 | { 47 | struct list_head *tmp, *iter; 48 | struct sbuf *cur; 49 | 50 | /* If it's in the list, delete it. */ 51 | list_for_each_safe_entry(cur, iter, tmp, hook, node) { 52 | if (!strcmp(sbuf_get(cur), sbuf_get(s))) { 53 | list_del(&cur->node); 54 | sbuf_free(cur); 55 | } 56 | } 57 | } 58 | 59 | void 60 | hook_run(struct list_head *hook) 61 | { 62 | struct sbuf *cur; 63 | cmdret *result; 64 | 65 | list_for_each_entry(cur, hook, node) { 66 | result = command(1, sbuf_get(cur)); 67 | if (result) { 68 | if (result->output) 69 | message(result->output); 70 | cmdret_free(result); 71 | } 72 | } 73 | } 74 | 75 | struct list_head * 76 | hook_lookup(char *s) 77 | { 78 | struct rp_hook_db_entry *entry; 79 | 80 | for (entry = rp_hook_db; entry->name; entry++) { 81 | if (!strcmp(s, entry->name)) { 82 | return entry->hook; 83 | } 84 | } 85 | 86 | return NULL; 87 | } 88 | -------------------------------------------------------------------------------- /hook.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef HOOKS_H 20 | #define HOOKS_H 21 | 22 | void hook_run(struct list_head *hook); 23 | void hook_remove(struct list_head *hook, struct sbuf *s); 24 | void hook_add(struct list_head *hook, struct sbuf *s); 25 | struct list_head *hook_lookup(char *s); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /input.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Read keyboard input from the user. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "sdorfehs.h" 31 | 32 | /* 33 | * Convert an X11 modifier mask to the rp modifier mask equivalent, as best it 34 | * can (the X server may not have a hyper key defined, for instance). 35 | */ 36 | unsigned int 37 | x11_mask_to_rp_mask(unsigned int mask) 38 | { 39 | unsigned int result = 0; 40 | 41 | PRINT_INPUT_DEBUG(("x11 mask = %x\n", mask)); 42 | 43 | result |= mask & ShiftMask ? RP_SHIFT_MASK : 0; 44 | result |= mask & ControlMask ? RP_CONTROL_MASK : 0; 45 | result |= mask & rp_modifier_info.meta_mod_mask ? RP_META_MASK : 0; 46 | result |= mask & rp_modifier_info.alt_mod_mask ? RP_ALT_MASK : 0; 47 | result |= mask & rp_modifier_info.hyper_mod_mask ? RP_HYPER_MASK : 0; 48 | result |= mask & rp_modifier_info.super_mod_mask ? RP_SUPER_MASK : 0; 49 | 50 | PRINT_INPUT_DEBUG(("rp mask = %x\n", mask)); 51 | 52 | return result; 53 | } 54 | 55 | /* 56 | * Convert an rp modifier mask to the x11 modifier mask equivalent, as best it 57 | * can (the X server may not have a hyper key defined, for instance). 58 | */ 59 | unsigned int 60 | rp_mask_to_x11_mask(unsigned int mask) 61 | { 62 | unsigned int result = 0; 63 | 64 | PRINT_INPUT_DEBUG(("rp mask = %x\n", mask)); 65 | 66 | result |= mask & RP_SHIFT_MASK ? ShiftMask : 0; 67 | result |= mask & RP_CONTROL_MASK ? ControlMask : 0; 68 | result |= mask & RP_META_MASK ? rp_modifier_info.meta_mod_mask : 0; 69 | result |= mask & RP_ALT_MASK ? rp_modifier_info.alt_mod_mask : 0; 70 | result |= mask & RP_HYPER_MASK ? rp_modifier_info.hyper_mod_mask : 0; 71 | result |= mask & RP_SUPER_MASK ? rp_modifier_info.super_mod_mask : 0; 72 | 73 | PRINT_INPUT_DEBUG(("x11 mask = %x\n", result)); 74 | 75 | return result; 76 | } 77 | 78 | /* Figure out what keysyms are attached to what modifiers */ 79 | void 80 | update_modifier_map(void) 81 | { 82 | unsigned int modmasks[] = { Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, 83 | Mod5Mask }; 84 | int row, col; /* The row and column in the modifier table. */ 85 | int found_alt_or_meta; 86 | XModifierKeymap *mods; 87 | int min_code, max_code; 88 | int syms_per_code; 89 | KeySym *syms; 90 | 91 | rp_modifier_info.meta_mod_mask = 0; 92 | rp_modifier_info.alt_mod_mask = 0; 93 | rp_modifier_info.super_mod_mask = 0; 94 | rp_modifier_info.hyper_mod_mask = 0; 95 | rp_modifier_info.num_lock_mask = 0; 96 | rp_modifier_info.scroll_lock_mask = 0; 97 | 98 | XDisplayKeycodes(dpy, &min_code, &max_code); 99 | syms = XGetKeyboardMapping(dpy, 100 | min_code, max_code - min_code + 1, 101 | &syms_per_code); 102 | mods = XGetModifierMapping(dpy); 103 | 104 | for (row = 3; row < 8; row++) { 105 | found_alt_or_meta = 0; 106 | for (col = 0; col < mods->max_keypermod; col++) { 107 | KeyCode code = 108 | mods->modifiermap[(row * mods->max_keypermod) + col]; 109 | int code_col; 110 | 111 | PRINT_INPUT_DEBUG(("row: %d col: %d code: %d\n", row, 112 | col, code)); 113 | 114 | if (code == 0) 115 | continue; 116 | 117 | /* Are any of this keycode's keysyms a meta key? */ 118 | for (code_col = 0; code_col < syms_per_code; code_col++) { 119 | int sym = syms[((code - min_code) * 120 | syms_per_code) + code_col]; 121 | 122 | switch (sym) { 123 | case XK_Meta_L: 124 | case XK_Meta_R: 125 | found_alt_or_meta = 1; 126 | rp_modifier_info.meta_mod_mask |= 127 | modmasks[row - 3]; 128 | PRINT_INPUT_DEBUG(("Found Meta on %d\n", 129 | rp_modifier_info.meta_mod_mask)); 130 | break; 131 | 132 | case XK_Alt_L: 133 | case XK_Alt_R: 134 | found_alt_or_meta = 1; 135 | rp_modifier_info.alt_mod_mask |= 136 | modmasks[row - 3]; 137 | PRINT_INPUT_DEBUG(("Found Alt on %d\n", 138 | rp_modifier_info.alt_mod_mask)); 139 | break; 140 | 141 | case XK_Super_L: 142 | case XK_Super_R: 143 | if (!found_alt_or_meta) { 144 | rp_modifier_info.super_mod_mask |= 145 | modmasks[row - 3]; 146 | PRINT_INPUT_DEBUG(("Found " 147 | "Super on %d\n", 148 | rp_modifier_info.super_mod_mask)); 149 | } 150 | code_col = syms_per_code; 151 | col = mods->max_keypermod; 152 | break; 153 | 154 | case XK_Hyper_L: 155 | case XK_Hyper_R: 156 | if (!found_alt_or_meta) { 157 | rp_modifier_info.hyper_mod_mask |= 158 | modmasks[row - 3]; 159 | PRINT_INPUT_DEBUG(("Found " 160 | "Hyper on %d\n", 161 | rp_modifier_info.hyper_mod_mask)); 162 | } 163 | code_col = syms_per_code; 164 | col = mods->max_keypermod; 165 | 166 | break; 167 | 168 | case XK_Num_Lock: 169 | rp_modifier_info.num_lock_mask |= 170 | modmasks[row - 3]; 171 | PRINT_INPUT_DEBUG(("Found NumLock on %d\n", 172 | rp_modifier_info.num_lock_mask)); 173 | break; 174 | 175 | case XK_Scroll_Lock: 176 | rp_modifier_info.scroll_lock_mask |= 177 | modmasks[row - 3]; 178 | PRINT_INPUT_DEBUG(("Found ScrollLock on %d\n", 179 | rp_modifier_info.scroll_lock_mask)); 180 | break; 181 | default: 182 | break; 183 | } 184 | } 185 | } 186 | } 187 | 188 | /* Stolen from Emacs 21.0.90 - xterm.c */ 189 | /* If we couldn't find any meta keys, accept any alt keys as meta keys. */ 190 | if (!rp_modifier_info.meta_mod_mask) { 191 | rp_modifier_info.meta_mod_mask = rp_modifier_info.alt_mod_mask; 192 | rp_modifier_info.alt_mod_mask = 0; 193 | } 194 | /* 195 | * If some keys are both alt and meta, make them just meta, not alt. 196 | */ 197 | if (rp_modifier_info.alt_mod_mask & rp_modifier_info.meta_mod_mask) { 198 | rp_modifier_info.alt_mod_mask &= ~rp_modifier_info.meta_mod_mask; 199 | } 200 | XFree((char *) syms); 201 | XFreeModifiermap(mods); 202 | } 203 | 204 | /* 205 | * we need a keycode + modifier to generate the proper keysym (such as @). 206 | * Return 1 if successful, 0 otherwise. This function can fail if a keysym 207 | * doesn't map to a keycode. 208 | */ 209 | static int 210 | keysym_to_keycode_mod(KeySym keysym, KeyCode * code, unsigned int *mod) 211 | { 212 | KeySym lower, upper; 213 | 214 | *mod = 0; 215 | *code = XKeysymToKeycode(dpy, keysym); 216 | lower = XkbKeycodeToKeysym(dpy, *code, 0, 0); 217 | upper = XkbKeycodeToKeysym(dpy, *code, 0, 1); 218 | /* 219 | * If you need to press shift to get the keysym, add the shift mask. 220 | */ 221 | if (upper == keysym && lower != keysym) 222 | *mod = ShiftMask; 223 | 224 | return *code != 0; 225 | } 226 | 227 | /* 228 | * Grab the key while ignoring annoying modifier keys including caps lock, num 229 | * lock, and scroll lock. 230 | */ 231 | void 232 | grab_key(KeySym keysym, unsigned int modifiers, Window grab_window) 233 | { 234 | unsigned int mod_list[8]; 235 | int i; 236 | KeyCode keycode; 237 | unsigned int mod; 238 | 239 | /* Convert to a modifier mask that X Windows will understand. */ 240 | modifiers = rp_mask_to_x11_mask(modifiers); 241 | if (!keysym_to_keycode_mod(keysym, &keycode, &mod)) 242 | return; 243 | PRINT_INPUT_DEBUG(("keycode_mod: %ld %d %d\n", keysym, keycode, mod)); 244 | modifiers |= mod; 245 | 246 | /* 247 | * Create a list of all possible combinations of ignored modifiers. 248 | * Assumes there are only 3 ignored modifiers. 249 | */ 250 | mod_list[0] = 0; 251 | mod_list[1] = LockMask; 252 | mod_list[2] = rp_modifier_info.num_lock_mask; 253 | mod_list[3] = mod_list[1] | mod_list[2]; 254 | mod_list[4] = rp_modifier_info.scroll_lock_mask; 255 | mod_list[5] = mod_list[1] | mod_list[4]; 256 | mod_list[6] = mod_list[2] | mod_list[4]; 257 | mod_list[7] = mod_list[1] | mod_list[2] | mod_list[4]; 258 | 259 | /* Grab every combination of ignored modifiers. */ 260 | for (i = 0; i < 8; i++) { 261 | XGrabKey(dpy, keycode, modifiers | mod_list[i], 262 | grab_window, True, GrabModeAsync, GrabModeAsync); 263 | } 264 | } 265 | 266 | 267 | /* Return the name of the keysym. caller must free returned pointer */ 268 | char * 269 | keysym_to_string(KeySym keysym, unsigned int modifier) 270 | { 271 | struct sbuf *name; 272 | char *tmp; 273 | 274 | name = sbuf_new(0); 275 | 276 | if (modifier & RP_SHIFT_MASK) 277 | sbuf_concat(name, "S-"); 278 | if (modifier & RP_CONTROL_MASK) 279 | sbuf_concat(name, "C-"); 280 | if (modifier & RP_META_MASK) 281 | sbuf_concat(name, "M-"); 282 | if (modifier & RP_ALT_MASK) 283 | sbuf_concat(name, "A-"); 284 | if (modifier & RP_HYPER_MASK) 285 | sbuf_concat(name, "H-"); 286 | if (modifier & RP_SUPER_MASK) 287 | sbuf_concat(name, "s-"); 288 | 289 | /* 290 | * On solaris machines (perhaps other machines as well) this call can 291 | * return NULL. In this case use the "NULL" string. 292 | */ 293 | tmp = XKeysymToString(keysym); 294 | if (tmp == NULL) 295 | tmp = "NULL"; 296 | 297 | sbuf_concat(name, tmp); 298 | 299 | return sbuf_free_struct(name); 300 | } 301 | 302 | /* 303 | * Cooks a keycode + modifier into a keysym + modifier. This should be used 304 | * anytime meaningful key information is to be extracted from a KeyPress or 305 | * KeyRelease event. 306 | * 307 | * returns the number of bytes in keysym_name. If you are not interested in the 308 | * keysym name pass in NULL for keysym_name and 0 for len. 309 | */ 310 | int 311 | cook_keycode(XKeyEvent *ev, KeySym *keysym, unsigned int *mod, 312 | char *keysym_name, int len, int ignore_bad_mods) 313 | { 314 | int nbytes; 315 | int shift = 0; 316 | KeySym lower, upper; 317 | 318 | if (ignore_bad_mods) { 319 | ev->state &= ~(LockMask 320 | | rp_modifier_info.num_lock_mask 321 | | rp_modifier_info.scroll_lock_mask); 322 | } 323 | if (len > 0) 324 | len--; 325 | nbytes = XLookupString(ev, keysym_name, len, keysym, NULL); 326 | 327 | /* Null terminate the string (not all X servers do it for us). */ 328 | if (keysym_name) { 329 | keysym_name[nbytes] = '\0'; 330 | } 331 | /* Find out if XLookupString gobbled the shift modifier */ 332 | if (ev->state & ShiftMask) { 333 | lower = XkbKeycodeToKeysym(dpy, ev->keycode, 0, 0); 334 | upper = XkbKeycodeToKeysym(dpy, ev->keycode, 0, 1); 335 | /* 336 | * If the keysym isn't affected by the shift key, then keep the 337 | * shift modifier. 338 | */ 339 | if (lower == upper) 340 | shift = ShiftMask; 341 | } 342 | *mod = ev->state; 343 | *mod &= (rp_modifier_info.meta_mod_mask 344 | | rp_modifier_info.alt_mod_mask 345 | | rp_modifier_info.hyper_mod_mask 346 | | rp_modifier_info.super_mod_mask 347 | | ControlMask 348 | | shift); 349 | 350 | return nbytes; 351 | } 352 | 353 | /* Wait for a key and discard it. */ 354 | void 355 | read_any_key(void) 356 | { 357 | char buffer[513]; 358 | unsigned int mod; 359 | KeySym c; 360 | 361 | read_single_key(&c, &mod, buffer, sizeof(buffer)); 362 | } 363 | 364 | /* The same as read_key, but handle focusing the key_window and reverting focus. */ 365 | int 366 | read_single_key(KeySym *keysym, unsigned int *modifiers, char *keysym_name, 367 | int len) 368 | { 369 | Window focus; 370 | int revert; 371 | int nbytes; 372 | 373 | XGetInputFocus(dpy, &focus, &revert); 374 | set_window_focus(rp_current_screen->key_window); 375 | nbytes = read_key(keysym, modifiers, keysym_name, len); 376 | set_window_focus(focus); 377 | 378 | return nbytes; 379 | } 380 | 381 | int 382 | read_key(KeySym *keysym, unsigned int *modifiers, char *keysym_name, int len) 383 | { 384 | XEvent ev; 385 | int nbytes; 386 | 387 | /* Read a key from the keyboard. */ 388 | do { 389 | XMaskEvent(dpy, KeyPressMask | KeyRelease, &ev); 390 | *modifiers = ev.xkey.state; 391 | nbytes = cook_keycode(&ev.xkey, keysym, modifiers, keysym_name, 392 | len, 0); 393 | } while (IsModifierKey(*keysym) || ev.xkey.type == KeyRelease); 394 | 395 | return nbytes; 396 | } 397 | 398 | static void 399 | update_input_window(rp_screen *s, rp_input_line *line) 400 | { 401 | int prompt_width, input_width, total_width; 402 | int char_len = 0, height; 403 | GC lgc; 404 | XGCValues gcv; 405 | 406 | prompt_width = rp_text_width(s, line->prompt, -1, NULL); 407 | input_width = rp_text_width(s, line->buffer, line->length, NULL); 408 | total_width = defaults.bar_x_padding * 2 + prompt_width + input_width + 409 | MAX_FONT_WIDTH(defaults.font); 410 | height = (FONT_HEIGHT(s) + defaults.bar_y_padding * 2); 411 | 412 | if (isu8start(line->buffer[line->position])) { 413 | do { 414 | char_len++; 415 | } while (isu8cont(line->buffer[line->position + char_len])); 416 | } else 417 | char_len = 1; 418 | 419 | if (total_width < defaults.input_window_size + prompt_width) 420 | total_width = defaults.input_window_size + prompt_width; 421 | 422 | if (defaults.bar_sticky) { 423 | XWindowAttributes attr; 424 | XGetWindowAttributes(dpy, s->bar_window, &attr); 425 | 426 | if (total_width < attr.width) 427 | total_width = attr.width; 428 | } 429 | 430 | XMoveResizeWindow(dpy, s->input_window, 431 | bar_x(s, total_width), bar_y(s, height), total_width, 432 | (FONT_HEIGHT(s) + defaults.bar_y_padding * 2)); 433 | 434 | XClearWindow(dpy, s->input_window); 435 | 436 | rp_draw_string(s, s->input_window, STYLE_NORMAL, 437 | defaults.bar_x_padding, 438 | defaults.bar_y_padding + FONT_ASCENT(s), 439 | line->prompt, 440 | -1, NULL, NULL); 441 | 442 | rp_draw_string(s, s->input_window, STYLE_NORMAL, 443 | defaults.bar_x_padding + prompt_width, 444 | defaults.bar_y_padding + FONT_ASCENT(s), 445 | line->buffer, 446 | line->length, NULL, NULL); 447 | 448 | gcv.function = GXxor; 449 | gcv.foreground = rp_glob_screen.fgcolor ^ rp_glob_screen.bgcolor; 450 | lgc = XCreateGC(dpy, s->input_window, GCFunction | GCForeground, &gcv); 451 | 452 | /* Draw a cheap-o cursor - MkIII */ 453 | XFillRectangle(dpy, s->input_window, lgc, 454 | defaults.bar_x_padding + prompt_width + 455 | rp_text_width(s, line->buffer, line->position, NULL), 456 | defaults.bar_y_padding, 457 | rp_text_width(s, &line->buffer[line->position], char_len, NULL), 458 | FONT_HEIGHT(s)); 459 | 460 | XFlush(dpy); 461 | XFreeGC(dpy, lgc); 462 | } 463 | 464 | char * 465 | get_input(char *prompt, int history_id, completion_fn fn) 466 | { 467 | return get_more_input(prompt, "", history_id, BASIC, fn); 468 | } 469 | 470 | char * 471 | get_more_input(char *prompt, char *preinput, int history_id, 472 | enum completion_styles style, completion_fn compl_fn) 473 | { 474 | /* Emacs 21 uses a 513 byte string to store the keysym name. */ 475 | char keysym_buf[513]; 476 | rp_screen *s = rp_current_screen; 477 | KeySym ch; 478 | unsigned int modifier; 479 | rp_input_line *line; 480 | char *final_input; 481 | edit_status status; 482 | Window focus; 483 | int revert, done = 0; 484 | 485 | history_reset(); 486 | 487 | /* Create our line structure */ 488 | line = input_line_new(prompt, preinput, history_id, style, compl_fn); 489 | 490 | /* Switch to the default colormap. */ 491 | if (current_window()) 492 | XUninstallColormap(dpy, current_window()->colormap); 493 | XInstallColormap(dpy, s->def_cmap); 494 | 495 | XMapWindow(dpy, s->input_window); 496 | XRaiseWindow(dpy, s->input_window); 497 | 498 | hide_bar(s, 1); 499 | 500 | XClearWindow(dpy, s->input_window); 501 | /* Switch focus to our input window to read the next key events. */ 502 | XGetInputFocus(dpy, &focus, &revert); 503 | set_window_focus(s->input_window); 504 | XSync(dpy, False); 505 | 506 | update_input_window(s, line); 507 | 508 | while (!done) { 509 | read_key(&ch, &modifier, keysym_buf, sizeof(keysym_buf)); 510 | modifier = x11_mask_to_rp_mask(modifier); 511 | PRINT_INPUT_DEBUG(("ch = %ld, modifier = %d, keysym_buf = %s\n", 512 | ch, modifier, keysym_buf)); 513 | status = execute_edit_action(line, ch, modifier, keysym_buf); 514 | 515 | switch (status) { 516 | case EDIT_COMPLETE: 517 | case EDIT_DELETE: 518 | case EDIT_INSERT: 519 | case EDIT_MOVE: 520 | /* 521 | * If the text changed (and we didn't just complete 522 | * something) then set the virgin bit. 523 | */ 524 | if (status != EDIT_COMPLETE) 525 | line->compl->virgin = 1; 526 | /* In all cases, we need to redisplay the input string. */ 527 | update_input_window(s, line); 528 | break; 529 | case EDIT_NO_OP: 530 | break; 531 | case EDIT_ABORT: 532 | final_input = NULL; 533 | done = 1; 534 | break; 535 | case EDIT_DONE: 536 | final_input = xstrdup(line->buffer); 537 | done = 1; 538 | break; 539 | default: 540 | errx(1, "unhandled input status %d; this is a *BUG*", 541 | status); 542 | } 543 | } 544 | 545 | /* Clean up our line structure */ 546 | input_line_free(line); 547 | 548 | /* Revert focus. */ 549 | set_window_focus(focus); 550 | 551 | /* Possibly restore colormap. */ 552 | if (current_window()) { 553 | XUninstallColormap(dpy, s->def_cmap); 554 | XInstallColormap(dpy, current_window()->colormap); 555 | } 556 | 557 | /* Re-show the sticky bar if we hid it earlier */ 558 | if (defaults.bar_sticky) 559 | hide_bar(s, 0); 560 | 561 | XUnmapWindow(dpy, s->input_window); 562 | 563 | /* 564 | * XXX: Without this, the input window doesn't show up again if we need 565 | * to prompt the user for more input, until the user types a character. 566 | * Figure out what is actually causing this and remove this. 567 | */ 568 | usleep(1); 569 | 570 | return final_input; 571 | } 572 | -------------------------------------------------------------------------------- /input.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Function prototypes. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_INPUT_H 21 | #define _SDORFEHS_INPUT_H 1 22 | 23 | char *keysym_to_string(KeySym keysym, unsigned int modifier); 24 | int cook_keycode(XKeyEvent *ev, KeySym *keysym, unsigned int *mod, 25 | char *keysym_name, int len, int ignore_bad_mods); 26 | char *get_input(char *prompt, int history_id, completion_fn fn); 27 | char *get_more_input(char *prompt, char *preinput, int history_id, 28 | enum completion_styles style, completion_fn fn); 29 | void read_any_key(void); 30 | int read_single_key(KeySym *keysym, unsigned int *modifiers, char *keysym_name, 31 | int len); 32 | int read_key(KeySym *keysym, unsigned int *modifiers, char *keysym_name, int len); 33 | unsigned int x11_mask_to_rp_mask(unsigned int mask); 34 | unsigned int rp_mask_to_x11_mask(unsigned int mask); 35 | void update_modifier_map(void); 36 | void grab_key(KeySym keysym, unsigned int modifiers, Window grab_window); 37 | 38 | void init_xkb(void); 39 | 40 | #endif /* ! _SDORFEHS_INPUT_H */ 41 | -------------------------------------------------------------------------------- /linkedlist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was taken from the Linux kernel and is Copyright (C) 2003 Linus 3 | * Torvalds 4 | * 5 | * Modified by Shawn Betts. Portions created by Shawn Betts are Copyright (C) 6 | * 2003, 2004 Shawn Betts 7 | * 8 | * This program is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by the Free 10 | * Software Foundation; either version 2 of the License, or (at your option) 11 | * any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 | * more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along with 19 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 20 | * Place, Suite 330, Boston, MA 02111-1307 USA. 21 | */ 22 | 23 | #include "linkedlist.h" 24 | 25 | /* 26 | * Insert a new entry between two known consecutive entries. 27 | * 28 | * This is only for internal list manipulation where we know 29 | * the prev/next entries already! 30 | */ 31 | void 32 | __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) 33 | { 34 | next->prev = new; 35 | new->next = next; 36 | new->prev = prev; 37 | prev->next = new; 38 | } 39 | 40 | /** 41 | * list_add - add a new entry 42 | * @new: new entry to be added 43 | * @head: list head to add it after 44 | * 45 | * Insert a new entry after the specified head. 46 | * This is good for implementing stacks. 47 | */ 48 | void 49 | list_add(struct list_head *new, struct list_head *head) 50 | { 51 | __list_add(new, head, head->next); 52 | } 53 | 54 | /** 55 | * list_add_tail - add a new entry 56 | * @new: new entry to be added 57 | * @head: list head to add it before 58 | * 59 | * Insert a new entry before the specified head. 60 | * This is useful for implementing queues. 61 | */ 62 | void 63 | list_add_tail(struct list_head *new, struct list_head *head) 64 | { 65 | __list_add(new, head->prev, head); 66 | } 67 | 68 | /* 69 | * Delete a list entry by making the prev/next entries 70 | * point to each other. 71 | * 72 | * This is only for internal list manipulation where we know 73 | * the prev/next entries already! 74 | */ 75 | void 76 | __list_del(struct list_head *prev, struct list_head *next) 77 | { 78 | next->prev = prev; 79 | prev->next = next; 80 | } 81 | 82 | /** 83 | * list_del - deletes entry from list. 84 | * @entry: the element to delete from the list. 85 | * Note: list_empty on entry does not return true after this, the entry is 86 | * in an undefined state. 87 | */ 88 | void 89 | list_del(struct list_head *entry) 90 | { 91 | __list_del(entry->prev, entry->next); 92 | } 93 | 94 | /** 95 | * list_del_init - deletes entry from list and reinitialize it. 96 | * @entry: the element to delete from the list. 97 | */ 98 | void 99 | list_del_init(struct list_head *entry) 100 | { 101 | __list_del(entry->prev, entry->next); 102 | INIT_LIST_HEAD(entry); 103 | } 104 | 105 | /** 106 | * list_move - delete from one list and add as another's head 107 | * @list: the entry to move 108 | * @head: the head that will precede our entry 109 | */ 110 | void 111 | list_move(struct list_head *list, struct list_head *head) 112 | { 113 | __list_del(list->prev, list->next); 114 | list_add(list, head); 115 | } 116 | 117 | /** 118 | * list_move_tail - delete from one list and add as another's tail 119 | * @list: the entry to move 120 | * @head: the head that will follow our entry 121 | */ 122 | void 123 | list_move_tail(struct list_head *list, struct list_head *head) 124 | { 125 | __list_del(list->prev, list->next); 126 | list_add_tail(list, head); 127 | } 128 | 129 | /** 130 | * list_empty - tests whether a list is empty 131 | * @head: the list to test. 132 | */ 133 | int 134 | list_empty(struct list_head *head) 135 | { 136 | return head->next == head; 137 | } 138 | 139 | void 140 | __list_splice(struct list_head *list, struct list_head *head) 141 | { 142 | struct list_head *first = list->next; 143 | struct list_head *last = list->prev; 144 | struct list_head *at = head->next; 145 | 146 | first->prev = head; 147 | head->next = first; 148 | 149 | last->next = at; 150 | at->prev = last; 151 | } 152 | 153 | /** 154 | * list_splice - join two lists 155 | * @list: the new list to add. 156 | * @head: the place to add it in the first list. 157 | */ 158 | void 159 | list_splice(struct list_head *list, struct list_head *head) 160 | { 161 | if (!list_empty(list)) 162 | __list_splice(list, head); 163 | } 164 | 165 | /** 166 | * list_splice_init - join two lists and reinitialise the emptied list. 167 | * @list: the new list to add. 168 | * @head: the place to add it in the first list. 169 | * 170 | * The list at @list is reinitialised 171 | */ 172 | void 173 | list_splice_init(struct list_head *list, struct list_head *head) 174 | { 175 | if (!list_empty(list)) { 176 | __list_splice(list, head); 177 | INIT_LIST_HEAD(list); 178 | } 179 | } 180 | 181 | int 182 | list_size(struct list_head *list) 183 | { 184 | struct list_head *cur; 185 | int i = 0; 186 | 187 | list_for_each(cur, list) 188 | i++; 189 | 190 | return i; 191 | } 192 | 193 | #define MAX_LIST_LENGTH_BITS 20 194 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) 195 | 196 | /* 197 | * Returns a list organized in an intermediate format suited 198 | * to chaining of merge() calls: null-terminated, no reserved or 199 | * sentinel head node, "prev" links not maintained. 200 | */ 201 | static struct list_head * 202 | merge(void *priv, int (*cmp)(void *priv, struct list_head *a, 203 | struct list_head *b), struct list_head *a, struct list_head *b) 204 | { 205 | struct list_head head, *tail = &head; 206 | 207 | while (a && b) { 208 | /* if equal, take 'a' -- important for sort stability */ 209 | if ((*cmp) (priv, a, b) <= 0) { 210 | tail->next = a; 211 | a = a->next; 212 | } else { 213 | tail->next = b; 214 | b = b->next; 215 | } 216 | tail = tail->next; 217 | } 218 | tail->next = a ? : b; 219 | return head.next; 220 | } 221 | 222 | /* 223 | * Combine final list merge with restoration of standard doubly-linked 224 | * list structure. This approach duplicates code from merge(), but 225 | * runs faster than the tidier alternatives of either a separate final 226 | * prev-link restoration pass, or maintaining the prev links 227 | * throughout. 228 | */ 229 | static void 230 | merge_and_restore_back_links(void *priv, int (*cmp)(void *priv, 231 | struct list_head *a, struct list_head *b), 232 | struct list_head *head, struct list_head *a, struct list_head *b) 233 | { 234 | struct list_head *tail = head; 235 | unsigned int count = 0; 236 | 237 | while (a && b) { 238 | /* if equal, take 'a' -- important for sort stability */ 239 | if ((*cmp) (priv, a, b) <= 0) { 240 | tail->next = a; 241 | a->prev = tail; 242 | a = a->next; 243 | } else { 244 | tail->next = b; 245 | b->prev = tail; 246 | b = b->next; 247 | } 248 | tail = tail->next; 249 | } 250 | tail->next = a ? : b; 251 | 252 | do { 253 | /* 254 | * In worst cases this loop may run many iterations. 255 | * Continue callbacks to the client even though no 256 | * element comparison is needed, so the client's cmp() 257 | * routine can invoke cond_resched() periodically. 258 | */ 259 | if (!(++count)) 260 | (*cmp) (priv, tail->next, tail->next); 261 | 262 | if (tail->next) { 263 | tail->next->prev = tail; 264 | tail = tail->next; 265 | } 266 | } while (tail->next); 267 | 268 | tail->next = head; 269 | head->prev = tail; 270 | } 271 | 272 | /** 273 | * list_sort - sort a list 274 | * @priv: private data, opaque to list_sort(), passed to @cmp 275 | * @head: the list to sort 276 | * @cmp: the elements comparison function 277 | * 278 | * This function implements "merge sort", which has O(nlog(n)) 279 | * complexity. 280 | * 281 | * The comparison function @cmp must return a negative value if @a 282 | * should sort before @b, and a positive value if @a should sort after 283 | * @b. If @a and @b are equivalent, and their original relative 284 | * ordering is to be preserved, @cmp must return 0. 285 | */ 286 | void 287 | list_sort(void *priv, struct list_head *head, 288 | int (*cmp)(void *priv, struct list_head *a, struct list_head *b)) 289 | { 290 | struct list_head *part[MAX_LIST_LENGTH_BITS + 1]; /* sorted partial lists 291 | * -- last slot is a 292 | * sentinel */ 293 | int lev; /* index into part[] */ 294 | int max_lev = 0; 295 | struct list_head *list; 296 | 297 | if (list_empty(head)) 298 | return; 299 | 300 | memset(part, 0, sizeof(part)); 301 | 302 | head->prev->next = NULL; 303 | list = head->next; 304 | 305 | while (list) { 306 | struct list_head *cur = list; 307 | list = list->next; 308 | cur->next = NULL; 309 | 310 | for (lev = 0; part[lev]; lev++) { 311 | cur = merge(priv, cmp, part[lev], cur); 312 | part[lev] = NULL; 313 | } 314 | if (lev > max_lev) { 315 | if (lev >= ARRAY_SIZE(part) - 1) { 316 | lev--; 317 | } 318 | max_lev = lev; 319 | } 320 | part[lev] = cur; 321 | } 322 | 323 | for (lev = 0; lev < max_lev; lev++) 324 | if (part[lev]) 325 | list = merge(priv, cmp, part[lev], list); 326 | 327 | merge_and_restore_back_links(priv, cmp, head, part[max_lev], list); 328 | } 329 | -------------------------------------------------------------------------------- /linkedlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was taken from the Linux kernel and is Copyright (C) 2003 Linus 3 | * Torvalds 4 | * 5 | * Modified by Shawn Betts. Portions created by Shawn Betts are Copyright (C) 6 | * 2003, 2004 Shawn Betts 7 | * 8 | * This program is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by the Free 10 | * Software Foundation; either version 2 of the License, or (at your option) 11 | * any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 | * more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along with 19 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 20 | * Place, Suite 330, Boston, MA 02111-1307 USA. 21 | */ 22 | 23 | #ifndef _SDORFEHS_LINKLIST_H 24 | #define _SDORFEHS_LINKLIST_H 25 | 26 | #include 27 | 28 | /* 29 | * Simple doubly linked list implementation. 30 | * 31 | * Some of the internal functions ("__xxx") are useful when 32 | * manipulating whole lists rather than single entries, as 33 | * sometimes we already know the next/prev entries and we can 34 | * generate better code by using them directly rather than 35 | * using the generic single-entry routines. 36 | */ 37 | 38 | struct list_head { 39 | struct list_head *next, *prev; 40 | }; 41 | 42 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 43 | 44 | #define LIST_HEAD(name) \ 45 | struct list_head name = LIST_HEAD_INIT(name) 46 | 47 | #define INIT_LIST_HEAD(ptr) do { \ 48 | (ptr)->next = (ptr); (ptr)->prev = (ptr); \ 49 | } while (0) 50 | 51 | /* Prototypes of C functions. */ 52 | int list_size(struct list_head * list); 53 | void list_splice_init(struct list_head *list, struct list_head *head); 54 | 55 | void list_splice(struct list_head *list, struct list_head *head); 56 | 57 | void __list_splice(struct list_head *list, struct list_head *head); 58 | 59 | int list_empty(struct list_head *head); 60 | 61 | void list_move_tail(struct list_head *list, struct list_head *head); 62 | 63 | void list_move(struct list_head *list, struct list_head *head); 64 | 65 | void list_del_init(struct list_head *entry); 66 | void list_del(struct list_head *entry); 67 | void __list_del(struct list_head *prev, struct list_head * next); 68 | void list_add_tail(struct list_head *new, struct list_head *head); 69 | void list_add(struct list_head *new, struct list_head *head); 70 | void __list_add(struct list_head *new, struct list_head *prev, 71 | struct list_head *next); 72 | 73 | #define prefetch(x) __builtin_prefetch(x) 74 | 75 | /* Return the last element in the list. */ 76 | #define list_last(last, head, member) \ 77 | { \ 78 | last = list_entry((head)->prev, typeof(*last), member); \ 79 | if (&last->member == (head)) \ 80 | last = NULL; \ 81 | } 82 | 83 | 84 | /** 85 | * container_of - cast a member of a structure out to the containing structure 86 | * 87 | * @ptr: the pointer to the member. 88 | * @type: the type of the container struct this is embedded in. 89 | * @member: the name of the member within the struct. 90 | * 91 | */ 92 | #define container_of(ptr, type, member) ({ \ 93 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 94 | (type *)( (char *)__mptr - offsetof(type,member) );}) 95 | 96 | /** 97 | * list_entry - get the struct for this entry 98 | * @ptr: the &struct list_head pointer. 99 | * @type: the type of the struct this is embedded in. 100 | * @member: the name of the list_struct within the struct. 101 | */ 102 | #define list_entry(ptr, type, member) \ 103 | container_of(ptr, type, member) 104 | 105 | 106 | /** 107 | * __list_for_each - iterate over a list 108 | * @pos: the &struct list_head to use as a loop counter. 109 | * @head: the head for your list. 110 | * 111 | * This variant differs from list_for_each() in that it's the 112 | * simplest possible list iteration code, no prefetching is done. 113 | * Use this for code that knows the list to be very short (empty 114 | * or 1 entry) most of the time. 115 | */ 116 | #define list_for_each(pos, head) \ 117 | for (pos = (head)->next; pos != (head); pos = pos->next) 118 | 119 | /** 120 | * list_for_each_prev - iterate over a list backwards 121 | * @pos: the &struct list_head to use as a loop counter. 122 | * @head: the head for your list. 123 | */ 124 | #define list_for_each_prev(pos, head) \ 125 | for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ 126 | pos = pos->prev, prefetch(pos->prev)) 127 | 128 | /** 129 | * list_for_each_safe - iterate over a list safe against removal of list entry 130 | * @pos: the &struct list_head to use as a loop counter. 131 | * @n: another &struct list_head to use as temporary storage 132 | * @head: the head for your list. 133 | */ 134 | #define list_for_each_safe(pos, n, head) \ 135 | for (pos = (head)->next, n = pos->next; pos != (head); \ 136 | pos = n, n = pos->next) 137 | 138 | #define list_for_each_safe_entry(item, pos, n, head, member) \ 139 | for (pos = (head)->next, \ 140 | item = list_entry(pos, typeof(*item), member), \ 141 | n = pos->next \ 142 | ; \ 143 | pos != (head) \ 144 | ; \ 145 | pos = n, \ 146 | item = list_entry(pos, typeof(*item), member), \ 147 | n = pos->next) \ 148 | 149 | /** 150 | * list_for_each_entry - iterate over list of given type 151 | * @pos: the type * to use as a loop counter. 152 | * @head: the head for your list. 153 | * @member: the name of the list_struct within the struct. 154 | */ 155 | #define list_for_each_entry(pos, head, member) \ 156 | for (pos = list_entry((head)->next, typeof(*pos), member), \ 157 | prefetch(pos->member.next); \ 158 | &pos->member != (head); \ 159 | pos = list_entry(pos->member.next, typeof(*pos), member), \ 160 | prefetch(pos->member.next)) 161 | 162 | #define list_for_each_entry_safe(pos, n, head, member) \ 163 | for (pos = list_entry((head)->next, typeof(*pos), member), \ 164 | n = list_entry(pos->member.next, typeof(*pos), member); \ 165 | &pos->member != (head); \ 166 | pos = n, \ 167 | n = list_entry(pos->member.next, typeof(*pos), member)) 168 | 169 | #define list_direction_entry(pos, head, member, direction) \ 170 | ({ \ 171 | typeof(pos) ret = NULL; \ 172 | struct list_head *a_head = head; \ 173 | if (pos->member.direction == a_head) { \ 174 | ret = list_entry(a_head->direction, \ 175 | typeof(*pos), member); \ 176 | } else { \ 177 | ret = list_entry(pos->member.direction, \ 178 | typeof(*pos), member); \ 179 | } \ 180 | ret; \ 181 | }) 182 | 183 | #define list_next_entry(pos, head, member) \ 184 | list_direction_entry(pos, head, member, next) 185 | 186 | #define list_prev_entry(pos, head, member) \ 187 | list_direction_entry(pos, head, member, prev) 188 | 189 | #define list_for_each_entry_prev(pos, head, member) \ 190 | for (pos = list_entry((head)->prev, typeof(*pos), member), \ 191 | prefetch(pos->member.prev); \ 192 | &pos->member != (head); \ 193 | pos = list_entry(pos->member.prev, typeof(*pos), member), \ 194 | prefetch(pos->member.prev)) 195 | 196 | #endif 197 | 198 | 199 | /* Return the first element in the list. */ 200 | #define list_first(first, head, member) \ 201 | { \ 202 | first = list_entry((head)->next, typeof(*first), member); \ 203 | if (&first->member == (head)) \ 204 | first = NULL; \ 205 | } 206 | 207 | void 208 | list_sort(void *priv, struct list_head * head, 209 | int (*cmp) (void *priv, struct list_head * a, 210 | struct list_head * b)); 211 | -------------------------------------------------------------------------------- /manage.h: -------------------------------------------------------------------------------- 1 | /* 2 | * manage.h 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_MANAGE_H 21 | #define _SDORFEHS_MANAGE_H 1 22 | 23 | #include "data.h" 24 | 25 | void clear_unmanaged_list(void); 26 | char *list_unmanaged_windows(void); 27 | void add_unmanaged_window(char *name); 28 | int unmanaged_window(Window w); 29 | void scanwins(void); 30 | void unmanage(rp_window *w); 31 | int update_window_name(rp_window *win); 32 | void update_normal_hints(rp_window *win); 33 | void rename_current_window(void); 34 | void set_state(rp_window *win, int state); 35 | long get_state(rp_window *win); 36 | void check_state(rp_window *win); 37 | 38 | int window_is_transient(rp_window *win); 39 | Atom get_net_wm_window_type(rp_window *win); 40 | int is_unmanaged_window_type(Window win); 41 | void update_window_information(rp_window *win); 42 | void map_window(rp_window *win); 43 | 44 | void maximize(rp_window *win); 45 | void force_maximize(rp_window *win); 46 | 47 | void grab_top_level_keys(Window w); 48 | void ungrab_top_level_keys(Window w); 49 | void ungrab_keys_all_wins(void); 50 | void grab_keys_all_wins(void); 51 | 52 | void hide_window(rp_window *win); 53 | void unhide_window(rp_window *win); 54 | void unhide_all_windows(void); 55 | void withdraw_window(rp_window *win); 56 | void hide_others(rp_window *win); 57 | void hide_vscreen_windows(rp_vscreen *v); 58 | void raise_utility_windows(void); 59 | 60 | #endif /* ! _SDORFEHS_MANAGE_H */ 61 | -------------------------------------------------------------------------------- /messages.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Messages. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_MESSAGES_H 21 | #define _SDORFEHS_MESSAGES_H 1 22 | 23 | #define MESSAGE_NO_OTHER_WINDOW "No other window" 24 | #define MESSAGE_NO_OTHER_FRAME "No other frame" 25 | #define MESSAGE_NO_MANAGED_WINDOWS "No managed windows" 26 | #define MESSAGE_UNKNOWN_COMMAND "Unknown command '%s'" 27 | #define MESSAGE_WINDOW_INFORMATION "This is window %d (%s)" 28 | 29 | #define MESSAGE_RAISE_TRANSIENT "Raise request from transient window %d (%s)" 30 | #define MESSAGE_RAISE_WINDOW "Raise request from window %d (%s)" 31 | #define MESSAGE_RAISE_TRANSIENT_VSCREEN "Raise request from transient window %d (%s) on vscreen %d" 32 | #define MESSAGE_RAISE_WINDOW_VSCREEN "Raise request from window %d (%s) on vscreen %d" 33 | #define MESSAGE_MAP_TRANSIENT "New transient window %d (%s)" 34 | #define MESSAGE_MAP_WINDOW "New window %d (%s)" 35 | #define MESSAGE_MAP_TRANSIENT_VSCREEN "New transient window %d (%s) on vscreen %d" 36 | #define MESSAGE_MAP_WINDOW_VSCREEN "New window %d (%s) on vscreen %d" 37 | 38 | #define MESSAGE_PROMPT_SWITCH_TO_WINDOW "Switch to window: " 39 | #define MESSAGE_PROMPT_NEW_WINDOW_NAME "Set window's title to: " 40 | #define MESSAGE_PROMPT_SHELL_COMMAND "/bin/sh -c " 41 | #define MESSAGE_PROMPT_COMMAND ":" 42 | #define MESSAGE_PROMPT_XTERM_COMMAND MESSAGE_PROMPT_SHELL_COMMAND TERM_PROG " -e " 43 | #define MESSAGE_PROMPT_SWITCH_TO_VSCREEN "Switch to vscreen: " 44 | #define MESSAGE_PROMPT_SELECT_VAR "Variable: " 45 | #define MESSAGE_PROMPT_VAR_VALUE "Value: " 46 | 47 | #define MESSAGE_WELCOME "Welcome to " PROGNAME " - press \"%s %s\" for help" 48 | 49 | #define EMPTY_FRAME_MESSAGE "Current Frame" 50 | 51 | #endif /* ! _SDORFEHS_MESSAGES_H */ 52 | -------------------------------------------------------------------------------- /number.c: -------------------------------------------------------------------------------- 1 | /* 2 | * handles the handing out of and uniqueness of window numbers. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "sdorfehs.h" 25 | 26 | /* Keep track of a set of numbers. For frames and windows. */ 27 | struct numset { 28 | /* A list of the numbers taken. */ 29 | int *numbers_taken; 30 | 31 | /* 32 | * the number of numbers currently stored in the numbers_taken array. 33 | */ 34 | int num_taken; 35 | 36 | /* the size of the numbers_taken array. */ 37 | int max_taken; 38 | }; 39 | 40 | /* Initialize a numset structure. */ 41 | static void 42 | numset_init(struct numset *ns) 43 | { 44 | ns->max_taken = 10; 45 | ns->num_taken = 0; 46 | 47 | ns->numbers_taken = xmalloc(ns->max_taken * sizeof(int)); 48 | } 49 | 50 | static int 51 | numset_num_is_taken(struct numset *ns, int n) 52 | { 53 | int i; 54 | 55 | for (i = 0; i < ns->num_taken; i++) { 56 | if (ns->numbers_taken[i] == n) 57 | return 1; 58 | } 59 | return 0; 60 | } 61 | 62 | /* returns index into numbers_taken that can be used. */ 63 | static int 64 | numset_find_empty_cell(struct numset *ns) 65 | { 66 | int i; 67 | 68 | for (i = 0; i < ns->num_taken; i++) { 69 | if (ns->numbers_taken[i] == -1) 70 | return i; 71 | } 72 | 73 | /* no vacant ones, so grow the array. */ 74 | if (ns->num_taken >= ns->max_taken) { 75 | ns->max_taken *= 2; 76 | ns->numbers_taken = xrealloc(ns->numbers_taken, 77 | sizeof(int) * ns->max_taken); 78 | } 79 | ns->num_taken++; 80 | 81 | return ns->num_taken - 1; 82 | } 83 | 84 | int 85 | numset_add_num(struct numset *ns, int n) 86 | { 87 | int ec; 88 | 89 | if (numset_num_is_taken(ns, n)) 90 | return 0; /* failed. */ 91 | 92 | /* 93 | * numset_find_empty_cell calls realloc on numbers_taken. So store the 94 | * ret val in ec then use ec as an index into the array. 95 | */ 96 | ec = numset_find_empty_cell(ns); 97 | ns->numbers_taken[ec] = n; 98 | return 1; /* success! */ 99 | } 100 | 101 | /* 102 | * returns a unique number that can be used as the window number in the program 103 | * bar. 104 | */ 105 | int 106 | numset_request(struct numset *ns) 107 | { 108 | int i; 109 | 110 | /* 111 | * look for a unique number, and add it to the list of taken numbers. 112 | */ 113 | i = 0; 114 | while (!numset_add_num(ns, i)) 115 | i++; 116 | 117 | return i; 118 | } 119 | 120 | /* 121 | * When a window is destroyed, it gives back its window number with this 122 | * function. 123 | */ 124 | void 125 | numset_release(struct numset *ns, int n) 126 | { 127 | int i; 128 | 129 | if (n < 0) 130 | warnx("ns=%p attempt to release %d!", ns, n); 131 | 132 | for (i = 0; i < ns->num_taken; i++) { 133 | if (ns->numbers_taken[i] == n) { 134 | ns->numbers_taken[i] = -1; 135 | return; 136 | } 137 | } 138 | } 139 | 140 | /* Create a new numset and return a pointer to it. */ 141 | struct numset * 142 | numset_new(void) 143 | { 144 | struct numset *ns; 145 | 146 | ns = xmalloc(sizeof(struct numset)); 147 | numset_init(ns); 148 | return ns; 149 | } 150 | 151 | /* Free a numset structure and it's internal data. */ 152 | void 153 | numset_free(struct numset *ns) 154 | { 155 | free(ns->numbers_taken); 156 | free(ns); 157 | } 158 | -------------------------------------------------------------------------------- /number.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Function prototypes. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_NUMBER_H 21 | #define _SDORFEHS_NUMBER_H 1 22 | 23 | struct numset; 24 | 25 | struct numset *numset_new(void); 26 | void numset_free(struct numset *ns); 27 | void numset_release(struct numset *ns, int n); 28 | int numset_request(struct numset *ns); 29 | int numset_add_num(struct numset *ns, int n); 30 | 31 | #endif /* ! _SDORFEHS_NUMBER_H */ 32 | -------------------------------------------------------------------------------- /sbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Functions for handling string buffers. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #include 21 | 22 | #include "sdorfehs.h" 23 | #include "sbuf.h" 24 | 25 | struct sbuf * 26 | sbuf_new(size_t initsz) 27 | { 28 | struct sbuf *b = xmalloc(sizeof(struct sbuf)); 29 | 30 | if (initsz < 1) 31 | initsz = 1; 32 | 33 | b->data = xmalloc(initsz); 34 | b->maxsz = initsz; 35 | 36 | b->data[0] = '\0'; 37 | b->len = 0; 38 | 39 | return b; 40 | } 41 | 42 | void 43 | sbuf_free(struct sbuf *b) 44 | { 45 | if (b != NULL) { 46 | free(b->data); 47 | free(b); 48 | } 49 | } 50 | 51 | /* Free the structure but return the string. */ 52 | char * 53 | sbuf_free_struct(struct sbuf *b) 54 | { 55 | if (b != NULL) { 56 | char *tmp; 57 | tmp = b->data; 58 | free(b); 59 | return tmp; 60 | } 61 | return NULL; 62 | } 63 | 64 | char * 65 | sbuf_nconcat(struct sbuf *b, const char *str, int len) 66 | { 67 | size_t minsz = b->len + len + 1; 68 | 69 | if (b->maxsz < minsz) { 70 | b->data = xrealloc(b->data, minsz); 71 | b->maxsz = minsz; 72 | } 73 | memcpy(b->data + b->len, str, minsz - b->len - 1); 74 | b->len = minsz - 1; 75 | *(b->data + b->len) = 0; 76 | 77 | return b->data; 78 | } 79 | 80 | 81 | char * 82 | sbuf_concat(struct sbuf *b, const char *str) 83 | { 84 | return sbuf_nconcat(b, str, strlen(str)); 85 | } 86 | 87 | char * 88 | sbuf_copy(struct sbuf *b, const char *str) 89 | { 90 | b->len = 0; 91 | return sbuf_concat(b, str); 92 | } 93 | 94 | char * 95 | sbuf_clear(struct sbuf *b) 96 | { 97 | b->len = 0; 98 | b->data[0] = '\0'; 99 | return b->data; 100 | } 101 | 102 | char * 103 | sbuf_get(struct sbuf *b) 104 | { 105 | return b->data; 106 | } 107 | 108 | char * 109 | sbuf_printf(struct sbuf *b, char *fmt,...) 110 | { 111 | va_list ap; 112 | 113 | free(b->data); 114 | 115 | va_start(ap, fmt); 116 | b->data = xvsprintf(fmt, ap); 117 | va_end(ap); 118 | 119 | b->len = strlen(b->data); 120 | b->maxsz = b->len + 1; 121 | 122 | return b->data; 123 | } 124 | 125 | char * 126 | sbuf_printf_concat(struct sbuf *b, char *fmt,...) 127 | { 128 | char *buffer; 129 | va_list ap; 130 | 131 | va_start(ap, fmt); 132 | buffer = xvsprintf(fmt, ap); 133 | va_end(ap); 134 | 135 | sbuf_concat(b, buffer); 136 | free(buffer); 137 | 138 | return b->data; 139 | } 140 | 141 | /* if width >= 0 then limit the width of s to width chars. */ 142 | char * 143 | sbuf_utf8_nconcat(struct sbuf *b, const char *s, int width) 144 | { 145 | if (width >= 0) { 146 | int len, nchars; 147 | 148 | len = nchars = 0; 149 | while (s[len] != '\0' && nchars < width) { 150 | if (isu8start(s[len])) 151 | do 152 | len++; 153 | while (isu8cont(s[len])); 154 | else 155 | len++; 156 | nchars++; 157 | } 158 | sbuf_printf_concat(b, "%.*s", len, s); 159 | } else 160 | sbuf_concat(b, s); 161 | 162 | return b->data; 163 | } 164 | 165 | void 166 | sbuf_chop(struct sbuf *b) 167 | { 168 | if (b->len) { 169 | b->data[--(b->len)] = '\0'; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /sbuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Function prototypes for handling string buffers. 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_SBUF_H 21 | #define _SDORFEHS_SBUF_H 1 22 | 23 | #include 24 | 25 | struct sbuf { 26 | char *data; 27 | size_t len; 28 | size_t maxsz; 29 | 30 | /* sbuf can exist in a list. */ 31 | struct list_head node; 32 | }; 33 | 34 | struct sbuf *sbuf_new(size_t initsz); 35 | void sbuf_free(struct sbuf *b); 36 | char *sbuf_free_struct(struct sbuf *b); 37 | char *sbuf_concat(struct sbuf *b, const char *str); 38 | char *sbuf_nconcat(struct sbuf *b, const char *str, int len); 39 | char *sbuf_utf8_nconcat(struct sbuf *b, const char *s, int width); 40 | char *sbuf_copy(struct sbuf *b, const char *str); 41 | char *sbuf_clear(struct sbuf *b); 42 | char *sbuf_get(struct sbuf *b); 43 | char *sbuf_printf(struct sbuf *b, char *fmt,...); 44 | char *sbuf_printf_concat(struct sbuf *b, char *fmt,...); 45 | void sbuf_chop(struct sbuf *b); 46 | 47 | #endif /* ! _SDORFEHS_SBUF_H */ 48 | -------------------------------------------------------------------------------- /screen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef SCREEN_H 20 | #define SCREEN_H 21 | 22 | int screen_bottom(rp_screen *s); 23 | int screen_top(rp_screen *s); 24 | int screen_right(rp_screen *s); 25 | int screen_left(rp_screen *s); 26 | int screen_height(rp_screen *s); 27 | int screen_width(rp_screen *s); 28 | 29 | rp_screen *find_screen(Window w); 30 | rp_screen *find_screen_by_attr(XWindowAttributes w); 31 | 32 | void init_screens(void); 33 | void activate_screen(rp_screen *s); 34 | void deactivate_screen(rp_screen *s); 35 | 36 | int is_rp_window(Window w); 37 | int is_a_root_window(unsigned int w); 38 | 39 | char *screen_dump(rp_screen *screen); 40 | 41 | void screen_update(rp_screen *s, int left, int top, int width, int height); 42 | void screen_update_frames(rp_screen *s); 43 | void screen_update_workarea(rp_screen *s); 44 | 45 | int screen_count(void); 46 | rp_screen *screen_primary(void); 47 | rp_screen *screen_next(void); 48 | rp_screen *screen_prev(void); 49 | 50 | rp_screen *screen_number(int number); 51 | 52 | void screen_sort(void); 53 | 54 | rp_screen *screen_add(int rr_output); 55 | void screen_del(rp_screen *s); 56 | void screen_free(rp_screen *s); 57 | void screen_free_final(void); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /sdorfehs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "sdorfehs.h" 39 | 40 | static void 41 | sighandler(int signum) 42 | { 43 | kill_signalled++; 44 | } 45 | 46 | static void 47 | hup_handler(int signum) 48 | { 49 | hup_signalled++; 50 | } 51 | 52 | static void 53 | alrm_handler(int signum) 54 | { 55 | alarm_signalled++; 56 | } 57 | 58 | static int 59 | handler(Display *d, XErrorEvent *e) 60 | { 61 | char error_msg[100]; 62 | 63 | if (e->request_code == X_ChangeWindowAttributes && 64 | e->error_code == BadAccess) 65 | errx(1, "another window manager is already running"); 66 | 67 | #ifdef IGNORE_BADWINDOW 68 | return 0; 69 | #else 70 | if (ignore_badwindow && e->error_code == BadWindow) 71 | return 0; 72 | #endif 73 | 74 | XGetErrorText(d, e->error_code, error_msg, sizeof(error_msg)); 75 | warnx("X error: %s", error_msg); 76 | 77 | /* 78 | * If there is already an error to report, replace it with this new one. 79 | */ 80 | free(rp_error_msg); 81 | rp_error_msg = xstrdup(error_msg); 82 | 83 | return 0; 84 | } 85 | 86 | static void 87 | print_help(void) 88 | { 89 | printf("%s %s\n", PROGNAME, VERSION); 90 | printf("usage: %s [-h]\n", PROGNAME); 91 | printf(" %s [-d dpy] [-c cmd] [-i] [-f file]\n", PROGNAME); 92 | exit(0); 93 | } 94 | 95 | static int 96 | read_startup_files(const char *alt_rcfile) 97 | { 98 | FILE *fileptr = NULL; 99 | char *config_dir, *filename; 100 | 101 | if (alt_rcfile) 102 | filename = strdup(alt_rcfile); 103 | else { 104 | config_dir = get_config_dir(); 105 | filename = xsprintf("%s/config", config_dir); 106 | free(config_dir); 107 | } 108 | 109 | fileptr = fopen(filename, "r"); 110 | if (fileptr == NULL && errno != ENOENT) 111 | warn("could not open %s", filename); 112 | free(filename); 113 | 114 | if (fileptr != NULL) { 115 | set_close_on_exec(fileno(fileptr)); 116 | read_rc_file(fileptr); 117 | fclose(fileptr); 118 | return 1; 119 | } 120 | 121 | return 0; 122 | } 123 | 124 | /* 125 | * Odd that we spend so much code on making sure the silly welcome message is 126 | * correct. Oh well... 127 | */ 128 | static void 129 | show_welcome_message(void) 130 | { 131 | rp_action *help_action; 132 | char *prefix, *help; 133 | rp_keymap *map; 134 | 135 | prefix = keysym_to_string(prefix_key.sym, prefix_key.state); 136 | 137 | map = find_keymap(ROOT_KEYMAP); 138 | 139 | /* Find the help key binding. */ 140 | help_action = find_keybinding_by_action("help " ROOT_KEYMAP, map); 141 | if (help_action) 142 | help = keysym_to_string(help_action->key, help_action->state); 143 | else 144 | help = NULL; 145 | 146 | 147 | if (help) { 148 | /* 149 | * A little kludge to use ? instead of `question' for the help key. 150 | */ 151 | if (!strcmp(help, "question")) 152 | marked_message_printf(0, 0, MESSAGE_WELCOME, prefix, "?"); 153 | else 154 | marked_message_printf(0, 0, MESSAGE_WELCOME, prefix, help); 155 | 156 | free(help); 157 | } else { 158 | marked_message_printf(0, 0, MESSAGE_WELCOME, prefix, ":help"); 159 | } 160 | 161 | free(prefix); 162 | } 163 | 164 | static void 165 | init_defaults(void) 166 | { 167 | unsigned long atom = 0; 168 | 169 | defaults.top_kmap = xstrdup(TOP_KEYMAP); 170 | 171 | defaults.win_gravity = NorthWestGravity; 172 | defaults.trans_gravity = CenterGravity; 173 | defaults.maxsize_gravity = CenterGravity; 174 | 175 | defaults.input_window_size = 200; 176 | defaults.window_border_width = 1; 177 | defaults.only_border = 1; 178 | defaults.bar_x_padding = 14; 179 | defaults.bar_y_padding = 10; 180 | defaults.bar_location = NorthWestGravity; 181 | defaults.bar_timeout = 3; 182 | defaults.bar_border_width = 0; 183 | defaults.bar_in_padding = 1; 184 | defaults.bar_sticky = 1; 185 | 186 | defaults.frame_indicator_timeout = 1; 187 | defaults.frame_resize_unit = 10; 188 | 189 | defaults.padding_left = 20; 190 | defaults.padding_right = 20; 191 | defaults.padding_top = 20; 192 | defaults.padding_bottom = 20; 193 | 194 | defaults.gap = 10; 195 | 196 | defaults.font_string = xstrdup(DEFAULT_XFT_FONT); 197 | 198 | defaults.fgcolor_string = xstrdup("#eeeeee"); 199 | defaults.bgcolor_string = xstrdup("black"); 200 | defaults.fwcolor_string = xstrdup("black"); 201 | defaults.bwcolor_string = xstrdup("black"); 202 | defaults.barbordercolor_string = xstrdup("#eeeeee"); 203 | 204 | defaults.wait_for_key_cursor = 1; 205 | 206 | defaults.window_fmt = xstrdup("%n%s%t"); 207 | defaults.info_fmt = xstrdup("(%H, %W) %n(%t)"); 208 | defaults.frame_fmt = xstrdup("Frame %f (%Wx%H)"); 209 | defaults.sticky_fmt = xstrdup("%t"); 210 | defaults.resize_fmt = xstrdup("Resize frame (%Wx%H)"); 211 | 212 | defaults.win_name = WIN_NAME_TITLE; 213 | defaults.startup_message = 1; 214 | defaults.warp = 0; 215 | defaults.window_list_style = STYLE_COLUMN; 216 | 217 | defaults.history_size = 20; 218 | defaults.frame_selectors = xstrdup(""); 219 | defaults.maxundos = 20; 220 | 221 | get_atom(DefaultRootWindow(dpy), _net_number_of_desktops, XA_CARDINAL, 222 | 0, &atom, 1, NULL); 223 | if (atom > 0) 224 | defaults.vscreens = (int)atom; 225 | else 226 | defaults.vscreens = 12; 227 | 228 | defaults.ignore_resize_hints = 0; 229 | defaults.win_add_cur_vscreen = 0; 230 | } 231 | 232 | int 233 | main(int argc, char *argv[]) 234 | { 235 | int c, fd; 236 | char **cmd = NULL; 237 | int cmd_count = 0; 238 | char *display = NULL; 239 | int interactive = 0; 240 | char *alt_rcfile = NULL; 241 | char pid[8]; 242 | 243 | setlocale(LC_CTYPE, ""); 244 | 245 | if (XSupportsLocale()) { 246 | if (!XSetLocaleModifiers("")) 247 | warnx("couldn't set X locale modifiers"); 248 | } else 249 | warnx("X doesn't seem to support your locale"); 250 | 251 | /* Parse the arguments */ 252 | myargv = argv; 253 | while ((c = getopt(argc, argv, "c:d:hif:")) != -1) { 254 | switch (c) { 255 | case 'c': 256 | cmd = xrealloc(cmd, sizeof(char *) * (cmd_count + 1)); 257 | cmd[cmd_count++] = xstrdup(optarg); 258 | break; 259 | case 'd': 260 | display = optarg; 261 | break; 262 | case 'f': 263 | alt_rcfile = optarg; 264 | break; 265 | case 'h': 266 | print_help(); 267 | break; 268 | case 'i': 269 | interactive = 1; 270 | break; 271 | default: 272 | warnx("unsupported option %c", c); 273 | print_help(); 274 | } 275 | } 276 | 277 | /* Report extra unparsed arguments. */ 278 | if (optind < argc) { 279 | warnx("bogus arguments:"); 280 | while (optind < argc) 281 | fprintf(stderr, "%s ", argv[optind++]); 282 | fputc('\n', stderr); 283 | exit(1); 284 | } 285 | if (!(dpy = XOpenDisplay(display))) 286 | errx(1, "can't open display %s", display); 287 | set_close_on_exec(ConnectionNumber(dpy)); 288 | 289 | /* forked commands should not get X console tty as their stdin */ 290 | fd = open("/dev/null", O_RDONLY); 291 | dup2(fd, STDIN_FILENO); 292 | close(fd); 293 | 294 | /* Set our own specific Atoms. */ 295 | rp_selection = XInternAtom(dpy, "RP_SELECTION", False); 296 | 297 | /* TEXT atoms */ 298 | xa_string = XA_STRING; 299 | xa_compound_text = XInternAtom(dpy, "COMPOUND_TEXT", False); 300 | xa_utf8_string = XInternAtom(dpy, "UTF8_STRING", False); 301 | 302 | init_control_socket_path(); 303 | 304 | if (cmd_count > 0) { 305 | int j, exit_status = 0; 306 | 307 | for (j = 0; j < cmd_count; j++) { 308 | if (!send_command(interactive, cmd[j])) 309 | exit_status = 1; 310 | free(cmd[j]); 311 | } 312 | 313 | free(cmd); 314 | XCloseDisplay(dpy); 315 | return exit_status; 316 | } 317 | 318 | /* For child processes to know */ 319 | snprintf(pid, sizeof(pid), "%d", getpid()); 320 | setenv("SDORFEHS_PID", pid, 1); 321 | 322 | /* Must be first */ 323 | register_atom(&_net_supported, "_NET_SUPPORTED"); 324 | 325 | register_atom(&wm_change_state, "WM_CHANGE_STATE"); 326 | register_atom(&wm_colormaps, "WM_COLORMAP_WINDOWS"); 327 | register_atom(&wm_delete, "WM_DELETE_WINDOW"); 328 | register_atom(&wm_name, "WM_NAME"); 329 | register_atom(&wm_protocols, "WM_PROTOCOLS"); 330 | register_atom(&wm_state, "WM_STATE"); 331 | register_atom(&wm_take_focus, "WM_TAKE_FOCUS"); 332 | 333 | register_atom(&_net_active_window, "_NET_ACTIVE_WINDOW"); 334 | register_atom(&_net_client_list, "_NET_CLIENT_LIST"); 335 | register_atom(&_net_client_list_stacking, "_NET_CLIENT_LIST_STACKING"); 336 | register_atom(&_net_current_desktop, "_NET_CURRENT_DESKTOP"); 337 | register_atom(&_net_number_of_desktops, "_NET_NUMBER_OF_DESKTOPS"); 338 | register_atom(&_net_workarea, "_NET_WORKAREA"); 339 | 340 | register_atom(&_net_wm_name, "_NET_WM_NAME"); 341 | register_atom(&_net_wm_pid, "_NET_WM_PID"); 342 | register_atom(&_net_wm_state, "_NET_WM_STATE"); 343 | register_atom(&_net_wm_state_fullscreen, "_NET_WM_STATE_FULLSCREEN"); 344 | register_atom(&_net_wm_window_type, "_NET_WM_WINDOW_TYPE"); 345 | register_atom(&_net_wm_window_type_dialog, "_NET_WM_WINDOW_TYPE_DIALOG"); 346 | register_atom(&_net_wm_window_type_dock, "_NET_WM_WINDOW_TYPE_DOCK"); 347 | register_atom(&_net_wm_window_type_splash, "_NET_WM_WINDOW_TYPE_SPLASH"); 348 | register_atom(&_net_wm_window_type_tooltip, 349 | "_NET_WM_WINDOW_TYPE_TOOLTIP"); 350 | register_atom(&_net_wm_window_type_utility, 351 | "_NET_WM_WINDOW_TYPE_UTILITY"); 352 | register_atom(&_net_supporting_wm_check, 353 | "_NET_SUPPORTING_WM_CHECK"); 354 | 355 | /* Setup signal handlers. */ 356 | XSetErrorHandler(handler); 357 | set_sig_handler(SIGALRM, alrm_handler); 358 | set_sig_handler(SIGTERM, sighandler); 359 | set_sig_handler(SIGINT, sighandler); 360 | set_sig_handler(SIGHUP, hup_handler); 361 | set_sig_handler(SIGCHLD, chld_handler); 362 | 363 | if (bar_mkfifo() == -1) 364 | return 1; 365 | 366 | /* Setup our internal structures */ 367 | init_defaults(); 368 | init_window_stuff(); 369 | init_xrandr(); 370 | init_screens(); 371 | 372 | update_modifier_map(); 373 | init_user_commands(); 374 | initialize_default_keybindings(); 375 | history_load(); 376 | init_bar(); 377 | 378 | scanwins(); 379 | 380 | c = read_startup_files(alt_rcfile); 381 | if (c == -1) 382 | return 1; 383 | if (c == 0) { 384 | /* No config file, just do something basic. */ 385 | cmdret *result; 386 | if ((result = command(0, "hsplit"))) 387 | cmdret_free(result); 388 | } 389 | 390 | /* Indicate to the user that we have booted. */ 391 | if (defaults.startup_message) 392 | show_welcome_message(); 393 | 394 | /* If no window has focus, give the key_window focus. */ 395 | if (current_window() == NULL) 396 | set_window_focus(rp_current_screen->key_window); 397 | 398 | #ifdef __OpenBSD__ 399 | /* cpath/wpath/fattr needed for history_save() */ 400 | pledge("stdio rpath cpath wpath fattr unix proc exec", NULL); 401 | #endif 402 | 403 | listen_for_commands(); 404 | listen_for_events(); 405 | 406 | return 0; 407 | } 408 | -------------------------------------------------------------------------------- /sdorfehs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Standard header 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_H 21 | #define _SDORFEHS_H 1 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #if defined(__BASE_FILE__) 35 | #define RP_FILE_NAME __BASE_FILE__ 36 | #else 37 | #define RP_FILE_NAME __FILE__ 38 | #endif 39 | 40 | /* Helper macro for error and debug reporting. */ 41 | #define PRINT_LINE(type) printf (PROGNAME ":%s:%d: %s: ",RP_FILE_NAME, __LINE__, #type) 42 | 43 | /* Debug reporting macros. */ 44 | #ifdef DEBUG 45 | #define PRINT_DEBUG(fmt) \ 46 | do { \ 47 | PRINT_LINE (debug); \ 48 | printf fmt; \ 49 | fflush (stdout); \ 50 | } while (0) 51 | #else 52 | #define PRINT_DEBUG(fmt) do {} while (0) 53 | #endif /* DEBUG */ 54 | 55 | #ifdef SENDCMD_DEBUG 56 | #define WARNX_DEBUG(fmt, ...) \ 57 | do { \ 58 | fprintf (stderr, fmt, __VA_ARGS__); \ 59 | fflush (stderr); \ 60 | } while (0) 61 | #else 62 | #define WARNX_DEBUG(fmt, ...) do {} while (0) 63 | #endif /* SENDCMD_DEBUG */ 64 | 65 | #ifdef INPUT_DEBUG 66 | #define PRINT_INPUT_DEBUG(fmt) \ 67 | do { \ 68 | PRINT_LINE (debug); \ 69 | printf fmt; \ 70 | fflush (stdout); \ 71 | } while (0) 72 | #else 73 | #define PRINT_INPUT_DEBUG(fmt) do {} while (0) 74 | #endif /* INPUT_DEBUG */ 75 | 76 | #include "config.h" 77 | 78 | #include "data.h" 79 | #include "globals.h" 80 | #include "manage.h" 81 | #include "window.h" 82 | #include "bar.h" 83 | #include "events.h" 84 | #include "number.h" 85 | #include "input.h" 86 | #include "messages.h" 87 | #include "communications.h" 88 | #include "sbuf.h" 89 | #include "split.h" 90 | #include "frame.h" 91 | #include "screen.h" 92 | #include "vscreen.h" 93 | #include "editor.h" 94 | #include "history.h" 95 | #include "completions.h" 96 | #include "hook.h" 97 | #include "xrandr.h" 98 | #include "format.h" 99 | #include "utf8.h" 100 | #include "util.h" 101 | 102 | #endif /* ! _SDORFEHS_H */ 103 | -------------------------------------------------------------------------------- /split.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef SPLIT_H 20 | #define SPLIT_H 21 | 22 | rp_window *set_frames_window(rp_frame *frame, rp_window *win); 23 | void cleanup_frame(rp_frame *frame); 24 | void maximize_all_windows_in_frame(rp_frame *frame); 25 | void maximize_frame(rp_frame *frame); 26 | void h_split_frame(rp_frame *frame, int pixels); 27 | void v_split_frame(rp_frame *frame, int pixels); 28 | void remove_all_splits(void); 29 | void resize_shrink_to_window(rp_frame *frame); 30 | void resize_frame_horizontally(rp_frame *frame, int diff); 31 | void resize_frame_vertically(rp_frame *frame, int diff); 32 | void remove_frame(rp_frame *frame); 33 | rp_window *find_window_for_frame(rp_frame *frame); 34 | rp_frame *find_windows_frame(rp_window *win); 35 | int num_frames(rp_vscreen *v); 36 | rp_frame *find_frame_next(rp_frame *frame); 37 | rp_frame *find_frame_prev(rp_frame *frame); 38 | rp_window *current_window(void); 39 | void init_frame_list(rp_vscreen *vscreen); 40 | void set_active_frame(rp_frame *frame, int force_indicator); 41 | void exchange_with_frame(rp_frame *cur, rp_frame *frame); 42 | void blank_frame(rp_frame *frame); 43 | void show_frame_indicator(int force); 44 | void hide_frame_indicator(void); 45 | 46 | void show_frame_message(char *msg); 47 | 48 | rp_frame *find_frame_right(rp_frame *frame); 49 | rp_frame *find_frame_left(rp_frame *frame); 50 | rp_frame *find_frame_down(rp_frame *frame); 51 | rp_frame *find_frame_up(rp_frame *frame); 52 | rp_frame *find_last_frame(rp_vscreen *v); 53 | rp_frame *find_frame_number(rp_vscreen *v, int num); 54 | 55 | rp_frame *current_frame(rp_vscreen *v); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /utf8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 3 | * Jeremie Courreges-Anglas 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #include "sdorfehs.h" 21 | 22 | #include 23 | 24 | int 25 | isu8char(char c) 26 | { 27 | return (c) & 0xC0; 28 | } 29 | 30 | int 31 | isu8start(char c) 32 | { 33 | return ((c) & 0xC0) == 0xC0; 34 | } 35 | 36 | int 37 | isu8cont(char c) 38 | { 39 | return ((c) & 0xC0) == 0x80; 40 | } 41 | -------------------------------------------------------------------------------- /utf8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 3 | * Jeremie Courreges-Anglas 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef UTF8_H 21 | #define UTF8_H 22 | 23 | extern int isu8char(char c); 24 | extern int isu8start(char c); 25 | extern int isu8cont(char c); 26 | 27 | extern int utf8_locale; 28 | 29 | int utf8_check_locale(void); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #include "sdorfehs.h" 20 | 21 | #include 22 | #include 23 | 24 | __dead void 25 | fatal(const char *msg) 26 | { 27 | fprintf(stderr, PROGNAME ": %s", msg); 28 | abort(); 29 | } 30 | 31 | void * 32 | xmalloc(size_t size) 33 | { 34 | void *value; 35 | 36 | value = malloc(size); 37 | if (value == NULL) 38 | fatal("Virtual memory exhausted"); 39 | return value; 40 | } 41 | 42 | void * 43 | xrealloc(void *ptr, size_t size) 44 | { 45 | void *value; 46 | 47 | value = realloc(ptr, size); 48 | if (value == NULL) 49 | fatal("Virtual memory exhausted"); 50 | return value; 51 | } 52 | 53 | char * 54 | xstrdup(const char *s) 55 | { 56 | char *value; 57 | value = strdup(s); 58 | if (value == NULL) 59 | fatal("Virtual memory exhausted"); 60 | return value; 61 | } 62 | 63 | /* Return a new string based on fmt. */ 64 | char * 65 | xvsprintf(char *fmt, va_list ap) 66 | { 67 | int size, nchars; 68 | char *buffer; 69 | va_list ap_copy; 70 | 71 | /* A reasonable starting value. */ 72 | size = strlen(fmt) + 1; 73 | buffer = xmalloc(size); 74 | 75 | while (1) { 76 | #if defined(va_copy) 77 | va_copy(ap_copy, ap); 78 | #elif defined(__va_copy) 79 | __va_copy(ap_copy, ap); 80 | #else 81 | /* 82 | * If there is no copy macro then this MAY work. On some 83 | * systems this could fail because va_list is a pointer so 84 | * assigning one to the other as below wouldn't make a copy of 85 | * the data, but just the pointer to the data. 86 | */ 87 | ap_copy = ap; 88 | #endif 89 | nchars = vsnprintf(buffer, size, fmt, ap_copy); 90 | #if defined(va_copy) || defined(__va_copy) 91 | va_end(ap_copy); 92 | #endif 93 | 94 | if (nchars > -1 && nchars < size) 95 | return buffer; 96 | else if (nchars > -1) 97 | size = nchars + 1; 98 | else { 99 | free(buffer); 100 | break; 101 | } 102 | 103 | /* Resize the buffer and try again. */ 104 | buffer = xrealloc(buffer, size); 105 | } 106 | 107 | return xstrdup(""); 108 | } 109 | 110 | /* Return a new string based on fmt. */ 111 | char * 112 | xsprintf(char *fmt,...) 113 | { 114 | char *buffer; 115 | va_list ap; 116 | 117 | va_start(ap, fmt); 118 | buffer = xvsprintf(fmt, ap); 119 | va_end(ap); 120 | 121 | return buffer; 122 | } 123 | 124 | /* strtok but do it for whitespace and be locale compliant. */ 125 | char * 126 | strtok_ws(char *s) 127 | { 128 | char *nonws; 129 | static char *last = NULL; 130 | 131 | if (s != NULL) 132 | last = s; 133 | else if (last == NULL) { 134 | warnx("strtok_ws() called but not initalized, this is a *BUG*"); 135 | abort(); 136 | } 137 | /* skip to first non-whitespace char. */ 138 | while (*last && isspace((unsigned char) *last)) 139 | last++; 140 | 141 | /* 142 | * If we reached the end of the string here then there is no more data. 143 | */ 144 | if (*last == '\0') 145 | return NULL; 146 | 147 | /* Now skip to the end of the data. */ 148 | nonws = last; 149 | while (*last && !isspace((unsigned char) *last)) 150 | last++; 151 | if (*last) { 152 | *last = '\0'; 153 | last++; 154 | } 155 | return nonws; 156 | } 157 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify it 3 | * under the terms of the GNU General Public License as published by the Free 4 | * Software Foundation; either version 2 of the License, or (at your option) 5 | * any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, but WITHOUT 8 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 9 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 10 | * more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along with 13 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 14 | * Place, Suite 330, Boston, MA 02111-1307 USA. 15 | */ 16 | 17 | #ifndef UTIL_H 18 | #define UTIL_H 19 | 20 | #ifndef __dead 21 | #define __dead __attribute__((__noreturn__)) 22 | #endif 23 | 24 | __dead void fatal(const char *msg); 25 | void *xmalloc(size_t size); 26 | void *xrealloc(void *ptr, size_t size); 27 | char *xstrdup(const char *s); 28 | char *xvsprintf(char *fmt, va_list ap); 29 | char *xsprintf(char *fmt,...); 30 | char *strtok_ws(char *s); 31 | int str_comp(char *s1, char *s2, size_t len); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /vscreen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 joshua stein 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef VSCREEN_H 20 | #define VSCREEN_H 21 | 22 | void init_vscreen(rp_vscreen *v, rp_screen *s); 23 | void vscreen_del(rp_vscreen *v); 24 | void vscreen_free(rp_vscreen *v); 25 | int vscreens_resize(int n); 26 | 27 | rp_vscreen *screen_find_vscreen_by_number(rp_screen *s, int n); 28 | rp_vscreen *screen_find_vscreen_by_name(rp_screen *s, char *name, int exact_match); 29 | 30 | struct list_head *vscreen_copy_frameset(rp_vscreen *v); 31 | void vscreen_restore_frameset(rp_vscreen *v, struct list_head *head); 32 | void vscreen_free_nums(rp_vscreen *v); 33 | void frameset_free(struct list_head *head); 34 | rp_frame *vscreen_get_frame(rp_vscreen *v, int frame_num); 35 | rp_frame *vscreen_find_frame_by_frame(rp_vscreen *v, rp_frame *f); 36 | 37 | void set_current_vscreen(rp_vscreen *v); 38 | void vscreen_move_window(rp_vscreen *v, rp_window *w); 39 | 40 | 41 | void vscreen_add_window(rp_vscreen *v, rp_window *w); 42 | void vscreen_resort_window(rp_vscreen *v, rp_window_elem *w); 43 | void vscreen_insert_window(struct list_head *h, rp_window_elem *w); 44 | 45 | void vscreen_del_window(rp_vscreen *v, rp_window *win); 46 | 47 | void vscreen_map_window(rp_vscreen *v, rp_window *win); 48 | 49 | void vscreen_unmap_window(rp_vscreen *v, rp_window *win); 50 | 51 | struct numset *vscreen_get_numset(rp_vscreen *v); 52 | void get_vscreen_list(rp_screen *s, char *delim, struct sbuf *buffer, 53 | int *mark_start, int *mark_end); 54 | 55 | rp_window *vscreen_prev_window(rp_vscreen *v, rp_window *win); 56 | rp_window *vscreen_next_window(rp_vscreen *v, rp_window *win); 57 | 58 | rp_window *vscreen_last_window(rp_vscreen *v); 59 | 60 | rp_vscreen *vscreen_prev_vscreen(rp_vscreen *v); 61 | rp_vscreen *vscreen_next_vscreen(rp_vscreen *v); 62 | rp_vscreen *screen_last_vscreen(rp_screen *screen); 63 | 64 | void vscreen_rename(rp_vscreen *v, char *name); 65 | 66 | rp_window_elem *vscreen_find_window(struct list_head *list, rp_window *win); 67 | rp_window_elem *vscreen_find_window_by_number(rp_vscreen *g, int num); 68 | 69 | void vscreen_move_window(rp_vscreen *to, rp_window *win); 70 | void vscreens_merge(rp_vscreen *from, rp_vscreen *to); 71 | 72 | void set_current_vscreen(rp_vscreen *v); 73 | 74 | rp_window *vscreen_last_window_by_class(rp_vscreen *v, char *class); 75 | rp_window *vscreen_last_window_by_class_complement(rp_vscreen *v, char *class); 76 | 77 | void vscreen_announce_current(rp_vscreen *v); 78 | 79 | #define rp_current_vscreen (rp_current_screen->current_vscreen) 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /window.h: -------------------------------------------------------------------------------- 1 | /* 2 | * functions for managing the window list 3 | * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #ifndef _SDORFEHS_LIST_H 21 | #define _SDORFEHS_LIST_H 1 22 | 23 | #include "sbuf.h" 24 | 25 | void free_window(rp_window *w); 26 | rp_window *add_to_window_list(rp_screen *s, Window w); 27 | void last_window(void); 28 | rp_window *find_window_in_list(Window w, struct list_head *list); 29 | rp_window *find_window(Window w); 30 | void maximize_current_window(void); 31 | void give_window_focus(rp_window *win, rp_window *last_win); 32 | void set_active_window(rp_window *win); 33 | void set_active_window_force(rp_window *win); 34 | void goto_window(rp_window *win); 35 | void update_window_gravity(rp_window *win); 36 | char *window_name(rp_window *win); 37 | 38 | /* int goto_window_name (char *name); */ 39 | rp_window *find_window_other(rp_vscreen *vscreen); 40 | rp_window *find_window_by_number(int n); 41 | rp_window *find_window_name(char *name, int exact_match); 42 | rp_window *find_window_number(int n); 43 | 44 | void insert_into_list(rp_window *win, struct list_head *list); 45 | 46 | void get_current_window_in_fmt(char *fmt, struct sbuf *buffer); 47 | void get_window_list(char *fmt, char *delim, struct sbuf *buffer, 48 | int *mark_start, int *mark_end); 49 | void init_window_stuff(void); 50 | void free_window_stuff(void); 51 | 52 | rp_frame *win_get_frame(rp_window *win); 53 | 54 | struct rp_child_info *get_child_info(Window w, int add); 55 | void change_windows_vscreen(rp_vscreen *v, rp_vscreen *new_vscreen); 56 | 57 | void window_full_screen(rp_window *win); 58 | 59 | #endif /* ! _SDORFEHS_LIST_H */ 60 | -------------------------------------------------------------------------------- /xrandr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * functions for grabbing information about xrandr screens 3 | * Copyright (C) 2016 Mathieu OTHACEHE 4 | * 5 | * This program is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License as published by the Free 7 | * Software Foundation; either version 2 of the License, or (at your option) 8 | * any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, but WITHOUT 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | * more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 | * Place, Suite 330, Boston, MA 02111-1307 USA. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include "sdorfehs.h" 24 | 25 | rp_screen *xrandr_screen_output(int rr_output); 26 | rp_screen *xrandr_screen_crtc(int rr_crtc); 27 | void xrandr_output_change(XRROutputChangeNotifyEvent *ev); 28 | void xrandr_crtc_change(XRRCrtcChangeNotifyEvent *ev); 29 | #ifdef DEBUG 30 | const char *xrandr_rotation_string(Rotation r); 31 | #endif 32 | 33 | static int xrandr_evbase; 34 | 35 | #define XRANDR_MAJOR 1 36 | #define XRANDR_MINOR 3 37 | 38 | void 39 | init_xrandr(void) 40 | { 41 | int errbase, major, minor; 42 | 43 | if (!XRRQueryExtension(dpy, &xrandr_evbase, &errbase)) { 44 | return; 45 | } 46 | if (XRRQueryVersion(dpy, &major, &minor) == 0) { 47 | return; 48 | } 49 | if (major != XRANDR_MAJOR || 50 | (major == XRANDR_MAJOR && minor < XRANDR_MINOR)) { 51 | warnx("Xrandr version %d.%d is not supported", major, minor); 52 | return; 53 | } 54 | XRRSelectInput(dpy, RootWindow(dpy, DefaultScreen(dpy)), 55 | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask); 56 | 57 | rp_have_xrandr = 1; 58 | } 59 | 60 | int 61 | xrandr_query_screen(int **outputs) 62 | { 63 | XRRScreenResources *res; 64 | XRROutputInfo *outinfo; 65 | int *output_array; 66 | int count = 0; 67 | int i; 68 | 69 | res = XRRGetScreenResources(dpy, RootWindow(dpy, DefaultScreen(dpy))); 70 | output_array = xmalloc(res->noutput * sizeof(int)); 71 | 72 | for (i = 0; i < res->noutput; i++) { 73 | outinfo = XRRGetOutputInfo(dpy, res, res->outputs[i]); 74 | if (!outinfo->crtc) 75 | continue; 76 | 77 | output_array[count++] = res->outputs[i]; 78 | 79 | XRRFreeOutputInfo(outinfo); 80 | } 81 | 82 | XRRFreeScreenResources(res); 83 | 84 | *outputs = output_array; 85 | return count; 86 | } 87 | 88 | rp_screen * 89 | xrandr_screen_output(int rr_output) 90 | { 91 | rp_screen *cur; 92 | 93 | list_for_each_entry(cur, &rp_screens, node) { 94 | if (cur->xrandr.output == rr_output) 95 | return cur; 96 | } 97 | 98 | return NULL; 99 | } 100 | 101 | rp_screen * 102 | xrandr_screen_crtc(int rr_crtc) 103 | { 104 | rp_screen *cur; 105 | 106 | list_for_each_entry(cur, &rp_screens, node) { 107 | if (cur->xrandr.crtc == rr_crtc) 108 | return cur; 109 | } 110 | 111 | return NULL; 112 | } 113 | 114 | int 115 | xrandr_is_primary(rp_screen *screen) 116 | { 117 | if (rp_have_xrandr) 118 | return screen->xrandr.primary; 119 | 120 | return 0; 121 | } 122 | 123 | void 124 | xrandr_fill_screen(int rr_output, rp_screen *screen) 125 | { 126 | XRRScreenResources *res; 127 | XRROutputInfo *outinfo; 128 | XRRCrtcInfo *crtinfo; 129 | RROutput primary; 130 | 131 | res = XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, 132 | DefaultScreen(dpy))); 133 | outinfo = XRRGetOutputInfo(dpy, res, rr_output); 134 | if (!outinfo || !outinfo->crtc) 135 | goto free_res; 136 | 137 | crtinfo = XRRGetCrtcInfo(dpy, res, outinfo->crtc); 138 | if (!crtinfo) 139 | goto free_out; 140 | 141 | primary = XRRGetOutputPrimary(dpy, RootWindow(dpy, DefaultScreen(dpy))); 142 | if (rr_output == primary) 143 | screen->xrandr.primary = 1; 144 | else 145 | screen->xrandr.primary = 0; 146 | 147 | if (screen->xrandr.name) 148 | free(screen->xrandr.name); 149 | screen->xrandr.name = xstrdup(outinfo->name); 150 | screen->xrandr.output = rr_output; 151 | screen->xrandr.crtc = outinfo->crtc; 152 | 153 | screen->left = crtinfo->x; 154 | screen->top = crtinfo->y; 155 | screen->width = crtinfo->width; 156 | screen->height = crtinfo->height; 157 | 158 | XRRFreeCrtcInfo(crtinfo); 159 | free_out: 160 | XRRFreeOutputInfo(outinfo); 161 | free_res: 162 | XRRFreeScreenResources(res); 163 | } 164 | 165 | void 166 | xrandr_output_change(XRROutputChangeNotifyEvent *ev) 167 | { 168 | XRRScreenResources *res; 169 | XRROutputInfo *outinfo; 170 | rp_screen *screen, *cur; 171 | 172 | res = XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, 173 | DefaultScreen(dpy))); 174 | outinfo = XRRGetOutputInfo(dpy, res, ev->output); 175 | 176 | screen = xrandr_screen_output(ev->output); 177 | 178 | /* bar might move if primary screen changed */ 179 | list_for_each_entry(cur, &rp_screens, node) 180 | hide_bar(cur, 1); 181 | 182 | mark_edge_frames(); 183 | 184 | if (!screen && outinfo && outinfo->crtc) { 185 | screen = screen_add(ev->output); 186 | screen_sort(); 187 | #ifdef DEBUG 188 | PRINT_DEBUG(("%s: Added screen %s with crtc %lu\n", __func__, 189 | screen->xrandr.name, 190 | (unsigned long) outinfo->crtc)); 191 | #else 192 | (void)screen; 193 | #endif 194 | } else if (screen && (!outinfo || !outinfo->crtc)) { 195 | PRINT_DEBUG(("%s: Removing screen %s\n", __func__, 196 | screen->xrandr.name)); 197 | screen_del(screen); 198 | } 199 | 200 | if (outinfo) 201 | XRRFreeOutputInfo(outinfo); 202 | XRRFreeScreenResources(res); 203 | 204 | list_for_each_entry(cur, &rp_screens, node) { 205 | xrandr_fill_screen(cur->xrandr.output, cur); 206 | screen_update_workarea(cur); 207 | screen_update_frames(cur); 208 | } 209 | 210 | redraw_sticky_bar_text(1); 211 | } 212 | 213 | #ifdef DEBUG 214 | const char * 215 | xrandr_rotation_string(Rotation r) 216 | { 217 | static char buf[64]; 218 | 219 | #define CASE(c) case c : return #c 220 | switch (r) { 221 | CASE(RR_Rotate_0); 222 | CASE(RR_Rotate_90); 223 | CASE(RR_Rotate_180); 224 | CASE(RR_Rotate_270); 225 | #undef CASE 226 | default: 227 | snprintf(buf, sizeof buf, "Unknown rotation %hu", 228 | (unsigned short)r); 229 | return buf; 230 | } 231 | } 232 | #endif 233 | 234 | void 235 | xrandr_crtc_change(XRRCrtcChangeNotifyEvent *ev) 236 | { 237 | rp_screen *screen; 238 | 239 | if (!ev->crtc || !ev->width || !ev->height) 240 | return; 241 | 242 | screen = xrandr_screen_crtc(ev->crtc); 243 | 244 | PRINT_DEBUG(("%s: crtc %s, rotation %s " 245 | "ev->x %d, ev->y %d, ev->width %d, ev->height %d\n", 246 | __func__, screen ? "found" : "not found", 247 | xrandr_rotation_string(ev->rotation), 248 | ev->x, ev->y, ev->width, ev->height)); 249 | 250 | if (!screen) 251 | return; 252 | 253 | if (ev->rotation == RR_Rotate_90 || ev->rotation == RR_Rotate_270) 254 | screen_update(screen, ev->x, ev->y, ev->height, ev->width); 255 | else 256 | screen_update(screen, ev->x, ev->y, ev->width, ev->height); 257 | } 258 | 259 | void 260 | xrandr_notify(XEvent *ev) 261 | { 262 | XRRNotifyEvent *n_event; 263 | XRROutputChangeNotifyEvent *o_event; 264 | XRRCrtcChangeNotifyEvent *c_event; 265 | 266 | if (ev->type != xrandr_evbase + RRNotify) 267 | return; 268 | 269 | PRINT_DEBUG(("--- Handling RRNotify ---\n")); 270 | 271 | n_event = (XRRNotifyEvent *) ev; 272 | switch (n_event->subtype) { 273 | case RRNotify_OutputChange: 274 | PRINT_DEBUG(("--- XRROutputChangeNotifyEvent ---\n")); 275 | o_event = (XRROutputChangeNotifyEvent *) ev; 276 | xrandr_output_change(o_event); 277 | break; 278 | case RRNotify_CrtcChange: 279 | PRINT_DEBUG(("--- XRRCrtcChangeNotifyEvent ---\n")); 280 | c_event = (XRRCrtcChangeNotifyEvent *) ev; 281 | xrandr_crtc_change(c_event); 282 | break; 283 | case RRNotify_OutputProperty: 284 | PRINT_DEBUG(("--- RRNotify_OutputProperty ---\n")); 285 | break; 286 | default: 287 | PRINT_DEBUG(("--- Unknown subtype %d ---\n", 288 | n_event->subtype)); 289 | break; 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /xrandr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Mathieu OTHACEHE 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the Free 6 | * Software Foundation; either version 2 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | * more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along with 15 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | * Place, Suite 330, Boston, MA 02111-1307 USA. 17 | */ 18 | 19 | #ifndef XRANDR_H 20 | #define XRANDR_H 21 | 22 | #include "sdorfehs.h" 23 | 24 | void init_xrandr(void); 25 | int xrandr_query_screen(int **outputs); 26 | int xrandr_is_primary(rp_screen * screen); 27 | void xrandr_fill_screen(int rr_output, rp_screen * screen); 28 | void xrandr_notify(XEvent * ev); 29 | 30 | #endif 31 | --------------------------------------------------------------------------------