wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

commit b306ac581ffff0955c1b662d07ae87a655dd8909
parent a490d34b5889b89ce08c08b83088887e83ce54d8
Author: Kris Maglione <jg@suckless.org>
Date:   Mon, 26 Mar 2007 00:48:00 -0400

New tree layout and make system. Note: This will currently *not* install configuration scripts.

Diffstat:
9menu.c | 527-------------------------------------------------------------------------------
LICENSE.p9p | 251-------------------------------------------------------------------------------
Makefile | 105+++++--------------------------------------------------------------------------
area.c | 438-------------------------------------------------------------------------------
bar.c | 134-------------------------------------------------------------------------------
client.c | 958-------------------------------------------------------------------------------
cmd/Makefile | 26++++++++++++++++++++++++++
cmd/util.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/Makefile | 26++++++++++++++++++++++++++
cmd/wmii/area.c | 438+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/bar.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/client.c | 958+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/column.c | 317+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/draw.c | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/event.c | 448+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/frame.c | 341+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/fs.c | 974+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/geom.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/key.c | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/main.c | 583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/mouse.c | 490+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/printevent.c | 997+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/printevent.h | 2++
cmd/wmii/rule.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/view.c | 446+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii/wmii.h | 439+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii9menu.c | 527+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmii9rc.sh | 19+++++++++++++++++++
cmd/wmiiloop.awk | 35+++++++++++++++++++++++++++++++++++
cmd/wmiir.c | 321+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cmd/wmiistartrc.sh | 13+++++++++++++
column.c | 317-------------------------------------------------------------------------------
config.mk | 45++++++++++++++++++++++++---------------------
draw.c | 227-------------------------------------------------------------------------------
event.c | 448-------------------------------------------------------------------------------
frame.c | 341-------------------------------------------------------------------------------
fs.c | 966-------------------------------------------------------------------------------
geom.c | 84-------------------------------------------------------------------------------
img/wmii.eps | 29+++++++++++++++++++++++++++++
img/wmii.mp | 19+++++++++++++++++++
include/Makefile | 6++++++
include/util.h | 36++++++++++++++++++++++++++++++++++++
key.c | 243-------------------------------------------------------------------------------
main.c | 596-------------------------------------------------------------------------------
man/Makefile | 14++++++++++++++
man/wmii.1 | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
man/wmiiloop.1 | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
man/wmiir.1 | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
man/wmiiwm.1 | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mk/common.mk | 18++++++++++++++++++
mk/dir.mk | 29+++++++++++++++++++++++++++++
mk/hdr.mk | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mk/lib.mk | 24++++++++++++++++++++++++
mk/man.mk | 7+++++++
mk/many.mk | 18++++++++++++++++++
mk/one.mk | 22++++++++++++++++++++++
mk/wmii.mk | 2++
mouse.c | 490-------------------------------------------------------------------------------
printevent.c | 997-------------------------------------------------------------------------------
printevent.h | 2--
rule.c | 86-------------------------------------------------------------------------------
util.c | 146-------------------------------------------------------------------------------
util/compile | 20++++++++++++++++++++
util/link | 31+++++++++++++++++++++++++++++++
view.c | 446-------------------------------------------------------------------------------
wmii.1 | 205-------------------------------------------------------------------------------
wmii.eps | 29-----------------------------
wmii.h | 451-------------------------------------------------------------------------------
wmii.mp | 19-------------------
wmii9rc | 19-------------------
wmiiloop | 35-----------------------------------
wmiiloop.1 | 79-------------------------------------------------------------------------------
wmiir | 2--
wmiir.1 | 51---------------------------------------------------
wmiistartrc | 13-------------
wmiiwm.1 | 150-------------------------------------------------------------------------------
76 files changed, 9172 insertions(+), 8870 deletions(-)

diff --git a/9menu.c b/9menu.c @@ -1,527 +0,0 @@ -/* Licence - * ======= - * - * 9menu is free software, and is Copyright (c) 1994 by David Hogan and - * Arnold Robbins. Permission is granted to all sentient beings to use - * this software, to make copies of it, and to distribute those copies, - * provided that: - * - * (1) the copyright and licence notices are left intact - * (2) the recipients are aware that it is free software - * (3) any unapproved changes in functionality are either - * (i) only distributed as patches - * or (ii) distributed as a new program which is not called 9menu - * and whose documentation gives credit where it is due - * (4) the authors are not held responsible for any defects - * or shortcomings in the software, or damages caused by it. - * - * There is no warranty for this software. Have a nice day. - * - * -- - * Arnold Robbins - * arnold@skeeve.com - * - * 9menu.c - * - * This program puts up a window that is just a menu, and executes - * commands that correspond to the items selected. - * - * Initial idea: Arnold Robbins - * Version using libXg: Matty Farrow (some ideas borrowed) - * This code by: David Hogan and Arnold Robbins - */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <X11/Xutil.h> - -#define nil ((void*)0) - -char version[] = "@(#) wmii9menu version 1.8"; - -/* lovely X stuff */ -Display *dpy; -int screen; -Window root; -Window menuwin; -Colormap defcmap; -XColor color; -XFontStruct *font; -GC gc; - -unsigned long selbg; -unsigned long selfg; -unsigned long normbg; -unsigned long normfg; -unsigned long border; -char *sfgname = nil; -char *sbgname = nil; -char *nfgname = nil; -char *nbgname = nil; -char *brcname = nil; - -/* for XSetWMProperties to use */ -int g_argc; -char **g_argv; - -/* for labels read from files */ -int f_argc; -char **f_argv; - -char *initial = ""; -int cur; - -char *fontlist[] = { /* default font list if no -font */ - "pelm.latin1.9", - "lucm.latin1.9", - "blit", - "9x15bold", - "9x15", - "lucidasanstypewriter-12", - "fixed", - nil -}; - -char *progname; /* my name */ -char *displayname; /* X display */ -char *fontname; /* font */ -char *filename; /* file to read options or labels from */ - -char **labels; /* list of labels and commands */ -char **commands; -int numitems; - -void usage(), run_menu(); -void create_window(); -void redraw(), warpmouse(); -void memory(); -int args(); - -/* memory --- print the out of memory message and die */ - -void -memory(s) -char *s; -{ - fprintf(stderr, "%s: couldn't allocate memory for %s\n", progname, s); - exit(1); -} - -/* args --- go through the argument list, set options */ - -int -args(int argc, char **argv) -{ - int i; - if (argc == 0 || argv == nil || argv[0] == '\0') - return -1; - for (i = 0; i < argc && argv[i] != nil; i++) { - if (strcmp(argv[i], "-display") == 0) - displayname = argv[++i]; - else if (strcmp(argv[i], "-file") == 0) - filename = argv[++i]; - else if (strcmp(argv[i], "-font") == 0) - fontname = argv[++i]; - else if (strncmp(argv[i], "-initial", 9) == 0) - initial = argv[++i]; - else if (strncmp(argv[i], "-nb", 3) == 0) - nbgname = argv[i][3] ? &argv[i][3] : argv[++i]; - else if (strncmp(argv[i], "-nf", 3) == 0) - nfgname = argv[i][3] ? &argv[i][3] : argv[++i]; - else if (strncmp(argv[i], "-sb", 3) == 0) - sbgname = argv[i][3] ? &argv[i][3] : argv[++i]; - else if (strncmp(argv[i], "-sf", 3) == 0) - sfgname = argv[i][3] ? &argv[i][3] : argv[++i]; - else if (strncmp(argv[i], "-br", 3) == 0) - brcname = argv[i][3] ? &argv[i][3] : argv[++i]; - else if (strcmp(argv[i], "-version") == 0) { - printf("%s\n", version); - exit(0); - } else if (argv[i][0] == '-') - usage(); - else - break; - } - return i; -} - -/* main --- crack arguments, set up X stuff, run the main menu loop */ - -int -main(int argc, char **argv) -{ - int i, j; - char *cp; - XGCValues gv; - unsigned long mask; - int nlabels = 0; - - g_argc = argc; - g_argv = argv; - - /* set default label name */ - if ((cp = strrchr(argv[0], '/')) == nil) - progname = argv[0]; - else - progname = ++cp; - - ++argv; - --argc; - - i = args(argc, argv); - - numitems = argc - i; - - if (numitems <= 0 && filename == nil) - usage(); - - if (filename) { - /* Read options and labels from file */ - char fbuf[1024]; - FILE *fp; - - fp = fopen(filename, "r"); - if (fp == nil) { - fprintf(stderr, "%s: couldn't open '%s'\n", progname, - filename); - exit(1); - } - while (fgets(fbuf, sizeof fbuf, fp)) { - char *s = fbuf; - strtok(s, "\n"); - if (s[0] == '-') { - char *temp[3]; - temp[0] = s; - temp[1] = strchr(s, ' '); - if (temp[1]) { - *(temp[1]++) = '\0'; - s = malloc(strlen(temp[1]) + 1); - if (s == nil) - memory("temporary argument"); - strcpy(s, temp[1]); - temp[1] = s; - } - temp[2] = 0; - args(temp[1] ? 2 : 1, temp); - continue; - } - if (s[0] == '#') - continue; - /* allow - in menu items to be escaped */ - if (s[0] == '\\') - ++s; - /* allocate space */ - if (f_argc < nlabels + 1) { - int k; - char **temp = malloc(sizeof(char *) * (f_argc + 5)); - if (temp == 0) - memory("temporary item"); - - for (k = 0; k < nlabels; k++) - temp[k] = f_argv[k]; - - free(f_argv); - f_argv = temp; - f_argc += 5; - } - f_argv[nlabels] = malloc(strlen(s) + 1); - if (f_argv[nlabels] == nil) - memory("temporary text"); - strcpy(f_argv[nlabels], s); - ++nlabels; - } - } - - labels = (char **) malloc((numitems + nlabels) * sizeof(char *)); - commands = (char **) malloc((numitems + nlabels) * sizeof(char *)); - if (commands == nil || labels == nil) - memory("command and label arrays"); - - for (j = 0; j < numitems; j++) { - labels[j] = argv[i + j]; - if ((cp = strchr(labels[j], ':')) != nil) { - *cp++ = '\0'; - commands[j] = cp; - } else - commands[j] = labels[j]; - if(strcmp(labels[j], initial) == 0) - cur = j; - } - - /* - * Now we no longer need i (our offset into argv) so we recycle it, - * while keeping the old value of j! - */ - for (i = 0; i < nlabels; i++) { - labels[j] = f_argv[i]; - if ((cp = strchr(labels[j], ':')) != nil) { - *cp++ = '\0'; - commands[j] = cp; - } else - commands[j] = labels[j]; - ++j; - } - - /* And now we merge the totals */ - numitems += nlabels; - - dpy = XOpenDisplay(displayname); - if (dpy == nil) { - fprintf(stderr, "%s: cannot open display", progname); - if (displayname != nil) - fprintf(stderr, " %s", displayname); - fprintf(stderr, "\n"); - exit(1); - } - screen = DefaultScreen(dpy); - root = RootWindow(dpy, screen); - /* - * This used to be - * black = BlackPixel(dpy, screen); - * white = WhitePixel(dpy, screen); - */ - defcmap = DefaultColormap(dpy, screen); - if (sbgname == nil - || XParseColor(dpy, defcmap, sbgname, &color) == 0 - || XAllocColor(dpy, defcmap, &color) == 0) - selbg = BlackPixel(dpy, screen); - else - selbg = color.pixel; - - if (sfgname == nil - || XParseColor(dpy, defcmap, sfgname, &color) == 0 - || XAllocColor(dpy, defcmap, &color) == 0) - selfg = WhitePixel(dpy, screen); - else - selfg = color.pixel; - - if (nbgname == nil - || XParseColor(dpy, defcmap, nbgname, &color) == 0 - || XAllocColor(dpy, defcmap, &color) == 0) - normbg = selfg; - else - normbg = color.pixel; - - if (nfgname == nil - || XParseColor(dpy, defcmap, nfgname, &color) == 0 - || XAllocColor(dpy, defcmap, &color) == 0) - normfg = selbg; - else - normfg = color.pixel; - - if (brcname == nil - || XParseColor(dpy, defcmap, brcname, &color) == 0 - || XAllocColor(dpy, defcmap, &color) == 0) - border = selbg; - else - border = color.pixel; - - /* try user's font first */ - if (fontname != nil) { - font = XLoadQueryFont(dpy, fontname); - if (font == nil) - fprintf(stderr, "%s: warning: can't load font %s\n", - progname, fontname); - } - - /* if no user font, try one of our default fonts */ - if (font == nil) { - for (i = 0; fontlist[i] != nil; i++) { - font = XLoadQueryFont(dpy, fontlist[i]); - if (font != nil) - break; - } - } - - if (font == nil) { - fprintf(stderr, "%s: fatal: cannot load a font\n", progname); - exit(1); - } - - gv.foreground = normfg; - gv.background = normbg; - gv.font = font->fid; - gv.line_width = 0; - mask = GCForeground | GCBackground | GCFont | GCLineWidth; - gc = XCreateGC(dpy, root, mask, &gv); - - run_menu(); - - XCloseDisplay(dpy); - exit(0); -} - -/* usage --- print a usage message and die */ - -void -usage() -{ - fprintf(stderr, "usage: %s [-display <displayname>] [-font <fontname>] ", progname); - fprintf(stderr, "[-file filename] "); - fprintf(stderr, "[-{n,s}{f,b} <color>] [-br <color>] "); - fprintf(stderr, "[-version] menitem[:command] ...\n"); - exit(0); -} - -/* run_menu --- put up the window, execute selected commands */ - -void -run_menu() -{ - XEvent ev; - int i, old, wide, high, dx, dy; - - dx = 0; - for (i = 0; i < numitems; i++) { - wide = XTextWidth(font, labels[i], strlen(labels[i])) + 4; - if (wide > dx) - dx = wide; - } - wide = dx; - - high = font->ascent + font->descent + 1; - dy = numitems * high; - - enum { - MouseMask = - ButtonPressMask - | ButtonReleaseMask - | ButtonMotionMask - | PointerMotionMask, - MenuMask = - MouseMask - | StructureNotifyMask - | ExposureMask - }; - - create_window(wide, high); - warpmouse(wide, high); - - XSelectInput(dpy, menuwin, MenuMask); - - XMapWindow(dpy, menuwin); - - i = 0; /* save menu Item position */ - - for (;;) { - XNextEvent(dpy, &ev); - switch (ev.type) { - default: - fprintf(stderr, "%s: unknown ev.type %d\n", - progname, ev.type); - break; - case ButtonRelease: - i = ev.xbutton.y/high; - if (ev.xbutton.x < 0 || ev.xbutton.x > wide) - return; - else if (i < 0 || i >= numitems) - return; - - printf("%s\n", commands[i]); - return; - case ButtonPress: - case MotionNotify: - old = cur; - cur = ev.xbutton.y/high; - if (ev.xbutton.x < 0 || ev.xbutton.x > wide) - cur = ~0; - if (cur == old) - break; - redraw(high, wide); - break; - case MapNotify: - redraw(high, wide); - if(XGrabPointer(dpy, menuwin, False, MouseMask, - GrabModeAsync, GrabModeAsync, - 0, None, CurrentTime - ) != GrabSuccess) { - fprintf(stderr, "Failed to grab the mouse\n"); - } - break; - case Expose: - redraw(high, wide); - break; - case MappingNotify: /* why do we get this? */ - break; - } - } -} - -/* set_wm_hints --- set all the window manager hints */ - -void -create_window(int wide, int high) -{ - XSetWindowAttributes wa = { 0 }; - unsigned int h; - int x, y, dummy; - Window wdummy; - - h = high * numitems; - - XQueryPointer(dpy, root, &wdummy, &wdummy, &x, &y, - &dummy, &dummy, (uint*)&dummy); - x -= wide / 2; - if (x < 0) - x = 0; - else if (x + wide > DisplayWidth(dpy, screen)) - x = DisplayWidth(dpy, screen) - wide; - - y -= cur * high + high / 2; - if (y < 0) - y = 0; - else if (y + h > DisplayHeight(dpy, screen)) - y = DisplayHeight(dpy, screen) - h; - - wa.override_redirect = True; - wa.border_pixel = border; - wa.background_pixel = normbg; - menuwin = XCreateWindow(dpy, root, x, y, wide, h, - 1, DefaultDepth(dpy, screen), CopyFromParent, - DefaultVisual(dpy, screen), - CWOverrideRedirect - | CWBackPixel - | CWBorderPixel - | CWEventMask, - &wa); - - XSetCommand(dpy, menuwin, g_argv, g_argc); -} - -/* redraw --- actually redraw the menu */ - -void -redraw(int high, int wide) -{ - int tx, ty, i; - - for (i = 0; i < numitems; i++) { - tx = (wide - XTextWidth(font, labels[i], strlen(labels[i]))) / 2; - ty = i*high + font->ascent + 1; - if (cur == i) - XSetForeground(dpy, gc, selbg); - else - XSetForeground(dpy, gc, normbg); - XFillRectangle(dpy, menuwin, gc, 0, i*high, wide, high); - if (cur == i) - XSetForeground(dpy, gc, selfg); - else - XSetForeground(dpy, gc, normfg); - XDrawString(dpy, menuwin, gc, tx, ty, labels[i], strlen(labels[i])); - } -} - -/* warpmouse --- bring the mouse to the menu */ - -void -warpmouse(int wide, int high) -{ - int offset; - - /* move tip of pointer into middle of menu item */ - offset = (font->ascent + font->descent + 1) / 2; - offset += 6; /* fudge factor */ - - XWarpPointer(dpy, None, menuwin, 0, 0, 0, 0, - wide/2, cur*high-high/2+offset); -} diff --git a/LICENSE.p9p b/LICENSE.p9p @@ -1,251 +0,0 @@ -The bulk of this software is derived from Plan 9 and is thus distributed -under the Lucent Public License, Version 1.02, reproduced below. - -There are a few exceptions: libutf, libfmt, and libregexp are distributed -under simpler BSD-like boilerplates. See the LICENSE files in those -directories. There are other exceptions, also marked with LICENSE files -in their directories. - -The bitmap fonts in the font/luc, font/lucm, font/lucsans, and font/pelm -directory are copyright B&H Inc. and distributed under more restricted -terms under agreement with B&H. See the NOTICE file in those directories. - -=================================================================== - -Lucent Public License Version 1.02 - -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC -LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE -PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - -1. DEFINITIONS - -"Contribution" means: - - a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original - Program, and - b. in the case of each Contributor, - - i. changes to the Program, and - ii. additions to the Program; - - where such changes and/or additions to the Program were added to the - Program by such Contributor itself or anyone acting on such - Contributor's behalf, and the Contributor explicitly consents, in - accordance with Section 3C, to characterization of the changes and/or - additions as Contributions. - -"Contributor" means LUCENT and any other entity that has Contributed a -Contribution to the Program. - -"Distributor" means a Recipient that distributes the Program, -modifications to the Program, or any part thereof. - -"Licensed Patents" mean patent claims licensable by a Contributor -which are necessarily infringed by the use or sale of its Contribution -alone or when combined with the Program. - -"Original Program" means the original version of the software -accompanying this Agreement as released by LUCENT, including source -code, object code and documentation, if any. - -"Program" means the Original Program and Contributions or any part -thereof - -"Recipient" means anyone who receives the Program under this -Agreement, including all Contributors. - -2. GRANT OF RIGHTS - - a. Subject to the terms of this Agreement, each Contributor hereby - grants Recipient a non-exclusive, worldwide, royalty-free copyright - license to reproduce, prepare derivative works of, publicly display, - publicly perform, distribute and sublicense the Contribution of such - Contributor, if any, and such derivative works, in source code and - object code form. - - b. Subject to the terms of this Agreement, each Contributor hereby - grants Recipient a non-exclusive, worldwide, royalty-free patent - license under Licensed Patents to make, use, sell, offer to sell, - import and otherwise transfer the Contribution of such Contributor, if - any, in source code and object code form. The patent license granted - by a Contributor shall also apply to the combination of the - Contribution of that Contributor and the Program if, at the time the - Contribution is added by the Contributor, such addition of the - Contribution causes such combination to be covered by the Licensed - Patents. The patent license granted by a Contributor shall not apply - to (i) any other combinations which include the Contribution, nor to - (ii) Contributions of other Contributors. No hardware per se is - licensed hereunder. - - c. Recipient understands that although each Contributor grants the - licenses to its Contributions set forth herein, no assurances are - provided by any Contributor that the Program does not infringe the - patent or other intellectual property rights of any other entity. Each - Contributor disclaims any liability to Recipient for claims brought by - any other entity based on infringement of intellectual property rights - or otherwise. As a condition to exercising the rights and licenses - granted hereunder, each Recipient hereby assumes sole responsibility - to secure any other intellectual property rights needed, if any. For - example, if a third party patent license is required to allow - Recipient to distribute the Program, it is Recipient's responsibility - to acquire that license before distributing the Program. - - d. Each Contributor represents that to its knowledge it has sufficient - copyright rights in its Contribution, if any, to grant the copyright - license set forth in this Agreement. - -3. REQUIREMENTS - -A. Distributor may choose to distribute the Program in any form under -this Agreement or under its own license agreement, provided that: - - a. it complies with the terms and conditions of this Agreement; - - b. if the Program is distributed in source code or other tangible - form, a copy of this Agreement or Distributor's own license agreement - is included with each copy of the Program; and - - c. if distributed under Distributor's own license agreement, such - license agreement: - - i. effectively disclaims on behalf of all Contributors all warranties - and conditions, express and implied, including warranties or - conditions of title and non-infringement, and implied warranties or - conditions of merchantability and fitness for a particular purpose; - ii. effectively excludes on behalf of all Contributors all liability - for damages, including direct, indirect, special, incidental and - consequential damages, such as lost profits; and - iii. states that any provisions which differ from this Agreement are - offered by that Contributor alone and not by any other party. - -B. Each Distributor must include the following in a conspicuous - location in the Program: - - Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights - Reserved. - -C. In addition, each Contributor must identify itself as the -originator of its Contribution in a manner that reasonably allows -subsequent Recipients to identify the originator of the Contribution. -Also, each Contributor must agree that the additions and/or changes -are intended to be a Contribution. Once a Contribution is contributed, -it may not thereafter be revoked. - -4. COMMERCIAL DISTRIBUTION - -Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use -of the Program, the Distributor who includes the Program in a -commercial product offering should do so in a manner which does not -create potential liability for Contributors. Therefore, if a -Distributor includes the Program in a commercial product offering, -such Distributor ("Commercial Distributor") hereby agrees to defend -and indemnify every Contributor ("Indemnified Contributor") against -any losses, damages and costs (collectively"Losses") arising from -claims, lawsuits and other legal actions brought by a third party -against the Indemnified Contributor to the extent caused by the acts -or omissions of such Commercial Distributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. -In order to qualify, an Indemnified Contributor must: a) promptly -notify the Commercial Distributor in writing of such claim, and b) -allow the Commercial Distributor to control, and cooperate with the -Commercial Distributor in, the defense and any related settlement -negotiations. The Indemnified Contributor may participate in any such -claim at its own expense. - -For example, a Distributor might include the Program in a commercial -product offering, Product X. That Distributor is then a Commercial -Distributor. If that Commercial Distributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Distributor's responsibility -alone. Under this section, the Commercial Distributor would have to -defend claims against the Contributors related to those performance -claims and warranties, and if a court requires any Contributor to pay -any damages as a result, the Commercial Distributor must pay those -damages. - -5. NO WARRANTY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY -WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement, including but not limited to -the risks and costs of program errors, compliance with applicable -laws, damage to or loss of data, programs or equipment, and -unavailability or interruption of operations. - -6. DISCLAIMER OF LIABILITY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR -ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. EXPORT CONTROL - -Recipient agrees that Recipient alone is responsible for compliance -with the United States export administration regulations (and the -export control laws and regulation of any other countries). - -8. GENERAL - -If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further -action by the parties hereto, such provision shall be reformed to the -minimum extent necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against a Contributor with -respect to a patent applicable to software (including a cross-claim or -counterclaim in a lawsuit), then any patent licenses granted by that -Contributor to such Recipient under this Agreement shall terminate as -of the date such litigation is filed. In addition, if Recipient -institutes patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Program -itself (excluding combinations of the Program with other software or -hardware) infringes such Recipient's patent(s), then such Recipient's -rights granted under Section 2(b) shall terminate as of the date such -litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of -time after becoming aware of such noncompliance. If all Recipient's -rights under this Agreement terminate, Recipient agrees to cease use -and distribution of the Program as soon as reasonably practicable. -However, Recipient's obligations under this Agreement and any licenses -granted by Recipient relating to the Program shall continue and -survive. - -LUCENT may publish new versions (including revisions) of this -Agreement from time to time. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new -version of the Agreement is published, Contributor may elect to -distribute the Program (including its Contributions) under the new -version. No one other than LUCENT has the right to modify this -Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, -Recipient receives no rights or licenses to the intellectual property -of any Contributor under this Agreement, whether expressly, by -implication, estoppel or otherwise. All rights in the Program not -expressly granted under this Agreement are reserved. - -This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No -party to this Agreement will bring a legal action under this Agreement -more than one year after the cause of action arose. Each party waives -its rights to a jury trial in any resulting litigation. - diff --git a/Makefile b/Makefile @@ -1,101 +1,8 @@ -# window manager improved 2 - window manager improved 2 -# © 2006 Anselm R. Garbe -# © 2006-2007 Kris Maglione -.POSIX: +ROOT=. +include ${ROOT}/mk/hdr.mk -include config.mk +DIRS = libixp \ + cmd \ + man -SRC = area.c bar.c client.c column.c draw.c event.c frame.c fs.c \ - geom.c key.c main.c mouse.c rule.c printevent.c util.c view.c -OBJ = ${SRC:.c=.o} -MAN1 = wmii wmiir wmiiwm wmiiloop -SCRIPTS = wmiistartrc wmiir wmiiloop wmii9rc -BIN = wmii wmii9menu - -all: options ${BIN} - -options: - @echo wmii build options: - @echo "CFLAGS = ${CFLAGS}" - @echo "LDFLAGS = ${LDFLAGS}" - @echo "CC = ${CC}" - -.c.o: - @echo CC $< - @${CC} -c ${CFLAGS} $< - -# In case this isn't from hg -.hg/00changelog.i: - @mkdir .hg - @touch .hg/00changelog.i -# VERSION must be updated on every commit/pull -config.mk: .hg/00changelog.i - -${OBJ}: wmii.h config.mk - -9menu.o: config.mk - -wmii: ${OBJ} - @echo LD $@ - @${CC} -o $@ ${OBJ} ${LDFLAGS} ${LIBIXP} - -wmii9menu: 9menu.o - @echo LD $@ - @${CC} -o $@ 9menu.o ${LDFLAGS} - -clean: - @echo cleaning - @rm -f ${BIN} ${OBJ} 9menu.o wmii-${VERSION}.tar.gz - -dist: clean - @echo creating dist tarball - @mkdir -p wmii-${VERSION} - @ln LICENSE Makefile config.mk README rc/ \ - wmii.eps wmii.mp \ - ${MAN1:=.1} ${SRC} ${SCRIPTS} wmii.h 9menu.c \ - wmii-${VERSION}/ - @tar -zcf wmii-${VERSION}.tgz wmii-${VERSION} - @rm -rf wmii-${VERSION} - -install: all - @echo installing executable files to ${DESTDIR}${PREFIX}/bin - @mkdir -p -m 0755 ${DESTDIR}${PREFIX}/bin - @for i in ${SCRIPTS}; do \ - sed "s|CONFPREFIX|${CONFPREFIX}|g; \ - s|CONFVERSION|${CONFVERSION}|g; \ - s|P9PATHS|${P9PATHS}|g; \ - s|AWKPATH|${AWKPATH}|g" \ - $$i \ - >${DESTDIR}${PREFIX}/bin/$$i; \ - chmod 755 ${DESTDIR}${PREFIX}/bin/$$i; \ - done - @for i in ${BIN}; do\ - cp -f $$i ${DESTDIR}${PREFIX}/bin; \ - chmod 755 ${DESTDIR}${PREFIX}/bin/$$i; \ - done - @echo installing scripts to ${DESTDIR}${CONFPREFIX}/wmii-${CONFVERSION} - @mkdir -p -m 0755 ${DESTDIR}${CONFPREFIX}/wmii-${CONFVERSION} - @cd rc; for i in *; do \ - sed "s|CONFPREFIX|${CONFPREFIX}|g" $$i \ - >${DESTDIR}${CONFPREFIX}/wmii-${CONFVERSION}/$$i; \ - chmod 755 ${DESTDIR}${CONFPREFIX}/wmii-${CONFVERSION}/$$i; \ - done - @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 - @mkdir -p -m 0755 ${DESTDIR}${MANPREFIX}/man1 - @for i in ${MAN1:=.1}; do \ - sed "s/VERSION/${VERSION}/g; \ - s|CONFPREFIX|${CONFPREFIX}|g" \ - $$i \ - >${DESTDIR}${MANPREFIX}/man1/$$i; \ - chmod 644 ${DESTDIR}${MANPREFIX}/man1/$$i; \ - done - -uninstall: - @echo removing executable files from ${DESTDIR}${PREFIX}/bin - @cd ${DESTDIR}${PREFIX}/bin && rm -f ${SCRIPTS} ${BIN} - @echo removing scripts from ${DESTDIR}${CONFPREFIX}/wmii-${CONFVERSION} - @rm -rf ${DESTDIR}${CONFPREFIX}/wmii-${CONFVERSION} - @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 - @cd ${DESTDIR}${MANPREFIX}/man1 && rm -f ${MAN1:=.1} - -.PHONY: all options clean dist install uninstall +include ${ROOT}/mk/dir.mk diff --git a/area.c b/area.c @@ -1,438 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <assert.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> -#include "wmii.h" - -static void place_frame(Frame *f); - -Client * -sel_client_of_area(Area *a) { - if(a && a->sel) - return a->sel->client; - return nil; -} - -Area * -create_area(View *v, Area *pos, uint w) { - static ushort id = 1; - uint area_num, col_num, i; - uint min_width; - Area *a, **p; - - min_width = screen->rect.width/NCOL; - p = pos ? &pos->next : &v->area; - - area_num = 0; - i = 0; - for(a = v->area; a != *p; a = a->next) - area_num++, i++; - for(; a; a = a->next) area_num++; - - col_num = max((area_num - 1), 0); - if(w == 0) { - if(col_num) { - w = newcolw_of_view(v); - if (w == 0) - w = screen->rect.width / (col_num + 1); - } - else w = screen->rect.width; - } - if(w < min_width) - w = min_width; - if(col_num && (col_num * min_width + w) > screen->rect.width) - return nil; - if(pos) - scale_view(v, screen->rect.width - w); - - a = emallocz(sizeof(Area)); - a->view = v; - a->id = id++; - a->rect = screen->rect; - a->rect.height = screen->rect.height - screen->brect.height; - a->mode = def.colmode; - a->rect.width = w; - a->frame = nil; - a->sel = nil; - a->next = *p; - *p = a; - - if(a == v->area) - a->floating = True; - if((!v->sel) || - (v->sel->floating && v->area->next == a && a->next == nil)) - focus_area(a); - - if(i) - write_event("CreateColumn %d\n", i); - return a; -} - -void -destroy_area(Area *a) { - Client *c; - Area *ta; - View *v; - uint i; - - v = a->view; - - if(a->frame) - fatal("destroying non-empty area"); - if(v->revert == a) - v->revert = nil; - for(c=client; c; c=c->next) - if(c->revert == a) - c->revert = nil; - - i = 1; - for(ta=v->area; ta; ta=ta->next) - if(ta->next == a) break; - else i++; - if(ta) { - ta->next = a->next; - if(ta->floating && ta->next) - ta = ta->next; - if(v->sel == a) - focus_area(ta); - } - if(i) write_event("DestroyColumn %d\n", i); - free(a); -} - -void -send_to_area(Area *to, Frame *f) { - Area *from; - assert(to->view == f->view); - from = f->area; - if(to->floating != from->floating) { - XRectangle temp = f->revert; - f->revert = f->rect; - f->rect = temp; - } - f->client->revert = from; - detach_from_area(f); - attach_to_area(to, f, True); -} - -void -attach_to_area(Area *a, Frame *f, Bool send) { - uint h, n_frame; - Frame *ft; - Client *c; - View *v; - - v = a->view; - c = f->client; - h = 0; - - f->area = a; - - n_frame = 0; - for(ft=a->frame; ft; ft=ft->anext) - n_frame++; - if(n_frame == 0) - n_frame = 1; - - c->floating = a->floating; - if(!a->floating) - f->rect.height = a->rect.height / n_frame; - - insert_frame(a->sel, f, False); - - if(a->floating) - place_frame(f); - - focus_frame(f, False); - resize_frame(f, &f->rect); - restack_view(a->view); - if(!a->floating) - arrange_column(a, False); - else - resize_frame(f, &f->rect); - - update_client_grab(f->client); - if(a->frame) - assert(a->sel); -} - -void -detach_from_area(Frame *f) { - Frame *pr; - Client *c; - Area *a; - View *v; - Area *ta; - uint i; - - a = f->area; - v = a->view; - c = f->client; - - for(pr = a->frame; pr; pr = pr->anext) - if(pr->anext == f) break; - remove_frame(f); - - if(a->sel == f) { - if(!pr) - pr = a->frame; - if((a->view->sel == a) && (pr)) - focus_frame(pr, False); - else - a->sel = pr; - } - - if(!a->floating) { - if(a->frame) - arrange_column(a, False); - else { - i = 0; - for(ta=v->area; ta && ta != a; ta=ta->next) - i++; - if(v->area->next->next) - destroy_area(a); - else if(!a->frame && v->area->frame) - /* focus floating area if it contains something */ - focus_area(v->area); - arrange_view(v); - } - } - else if(!a->frame) { - if(c->trans) { - /* focus area of transient, if possible */ - Client *cl = client_of_win(c->trans); - if(cl && cl->frame) { - a = cl->sel->area; - if(a->view == v) - focus_area(a); - } - } - else if(v->area->next->frame) - focus_area(v->area->next); - }else - assert(a->sel); -} - -static void -bit_twiddle(uint *field, uint width, uint x, uint y, Bool set) { - enum { devisor = sizeof(uint) * 8 }; - uint bx, mask; - - bx = x / devisor; - mask = 1 << x % devisor; - if(set) - field[y*width + bx] |= mask; - else - field[y*width + bx] &= ~mask; -} - -static Bool -bit_get(uint *field, uint width, uint x, uint y) { - enum { devisor = sizeof(uint) * 8 }; - uint bx, mask; - - bx = x / devisor; - mask = 1 << x % devisor; - - return (field[y*width + bx] & mask) != 0; -} - -static void -place_frame(Frame *f) { - enum { devisor = sizeof(uint) * 8 }; - enum { dx = 8, dy = 8 }; - static uint mwidth, mx, my; - static uint *field = nil; - BlitzAlign align; - XPoint p1 = {0, 0}; - XPoint p2 = {0, 0}; - XRectangle *rects; - Frame *fr; - Client *c; - Area *a; - Bool fit; - uint i, j, x, y, cx, cy, maxx, maxy, diff, num; - int snap; - - snap = screen->rect.height / 66; - num = 0; - fit = False; - align = CENTER; - - a = f->area; - c = f->client; - - if(c->trans) - return; - if(c->rect.width >= a->rect.width - || c->rect.height >= a->rect.height - || c->size.flags & USPosition - || c->size.flags & PPosition) - return; - if(!field) { - mx = screen->rect.width / dx; - my = screen->rect.height / dy; - mwidth = ceil((float)mx / devisor); - field = emallocz(sizeof(uint) * mwidth * my); - } - memset(field, ~0, (sizeof(uint) * mwidth * my)); - for(fr=a->frame; fr; fr=fr->anext) { - if(fr == f) { - cx = f->rect.width / dx; - cy = f->rect.height / dy; - continue; - } - if(fr->rect.x < 0) - x = 0; - else - x = fr->rect.x / dx; - if(fr->rect.y < 0) - y = 0; - else - y = fr->rect.y / dy; - maxx = r_east(&fr->rect) / dx; - maxy = r_south(&fr->rect) / dy; - for(j = y; j < my && j < maxy; j++) - for(i = x; i < mx && i < maxx; i++) - bit_twiddle(field, mwidth, i, j, False); - } - for(y = 0; y < my; y++) - for(x = 0; x < mx; x++) { - if(bit_get(field, mwidth, x, y)) { - for(i = x; (i < mx) && bit_get(field, mwidth, i, y); i++); - for(j = y; (j < my) && bit_get(field, mwidth, x, j); j++); - if(((i - x) * (j - y) > (p2.x - p1.x) * (p2.y - p1.y)) - && (i - x > cx) && (j - y > cy)) - { - fit = True; - p1.x = x; - p1.y = y; - p2.x = i; - p2.y = j; - } - } - } - if(fit) { - p1.x *= dx; - p1.y *= dy; - } - if(fit && (p1.x + f->rect.width < r_south(&a->rect))) - f->rect.x = p1.x; - else { - diff = a->rect.width - f->rect.width; - f->rect.x = a->rect.x + (random() % (diff ? diff : 1)); - } - if(fit && (p1.y + f->rect.height < (r_south(&a->rect)))) - f->rect.y = p1.y; - else { - diff = a->rect.height - f->rect.height; - f->rect.y = a->rect.y + (random() % (diff ? diff : 1)); - } - - rects = rects_of_view(a->view, &num, nil); - snap_rect(rects, num, &f->rect, &align, snap); - if(rects) - free(rects); -} - -void -focus_area(Area *a) { - Frame *f; - View *v; - Area *old_a; - int i; - - v = a->view; - f = a->sel; - old_a = v->sel; - - v->sel = a; - - if((old_a) - && (a->floating != old_a->floating)) - v->revert = old_a; - - if(v != screen->sel) - return; - - if(f) - focus_client(f->client); - else - focus_client(nil); - - if(a != old_a) { - i = 0; - for(a = v->area; a != v->sel; a = a->next) - i++; - if(i) - write_event("ColumnFocus %d\n", i); - else - write_event("FocusFloating\n"); - if(a->frame) - write_event("ClientFocus 0x%x\n", a->sel->client->win); - } -} - -char * -select_area(Area *a, char *arg) { - Area *new; - uint i; - Frame *p, *f; - View *v; - static char Ebadvalue[] = "bad value"; - - v = a->view; - f = a->sel; - if(!strncmp(arg, "toggle", 7)) { - if(!a->floating) - new = v->area; - else if(v->revert) - new = v->revert; - else - new = v->area->next; - } else if(!strncmp(arg, "left", 5)) { - if(a->floating) - return Ebadvalue; - for(new=v->area->next; new->next; new=new->next) - if(new->next == a) break; - } else if(!strncmp(arg, "right", 5)) { - if(a->floating) - return Ebadvalue; - new = a->next ? a->next : v->area->next; - } - else if(!strncmp(arg, "up", 3)) { - if(!f) - return Ebadvalue; - for(p=a->frame; p->anext; p=p->anext) - if(p->anext == f) break; - goto focus_frame; - } - else if(!strncmp(arg, "down", 5)) { - if(!f) - return Ebadvalue; - p = f->anext ? f->anext : a->frame; - goto focus_frame; - } - else if(!strncmp(arg, "~", 2)) { - new = v->area; - } - else { - if(sscanf(arg, "%u", &i) != 1 || i == 0) - return Ebadvalue; - for(new=v->area->next; new->next; new=new->next) - if(!--i) break; - } - focus_area(new); - return nil; - -focus_frame: - focus_frame(p, False); - frame_to_top(p); - if(v == screen->sel) - restack_view(v); - return nil; -} diff --git a/bar.c b/bar.c @@ -1,134 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <string.h> -#include "wmii.h" - -Bar *free_bars = nil; - -Bar * -create_bar(Bar **b_link, char *name) { - static uint id = 1; - Bar **i, *b = bar_of_name(*b_link, name);; - - if(b) - return b; - if(free_bars) { - b = free_bars; - free_bars = b->next; - memset(b, 0, sizeof(*b)); - } - else - b = emallocz(sizeof(Bar)); - b->id = id++; - strncpy(b->name, name, sizeof(b->name)); - b->brush = screen->bbrush; - b->brush.color = def.normcolor; - for(i = b_link; *i; i = &(*i)->next) - if(strcmp((*i)->name, name) >= 0) - break; - b->next = *i; - *i = b; - - return b; -} - -void -destroy_bar(Bar **b_link, Bar *b) { - Bar **p; - - for(p = b_link; *p; p = &(*p)->next) - if(*p == b) break; - *p = b->next; - b->next = free_bars; - free_bars = b; -} - -void -resize_bar(WMScreen *s) { - View *v; - - s->brect = s->rect; - s->brect.height = labelh(&def.font); - s->brect.y = s->rect.height - s->brect.height; - XMoveResizeWindow(blz.dpy, s->barwin, s->brect.x, s->brect.y, s->brect.width, s->brect.height); - XSync(blz.dpy, False); - draw_bar(s); - for(v = view; v; v = v->next) - arrange_view(v); -} - -void -draw_bar(WMScreen *s) { - uint width, tw, nb, size; - float shrink; - Bar *b, *tb, *largest, **pb; - - draw_tile(&s->bbrush); - if(!s->bar[BarLeft] && !s->bar[BarRight]) - goto MapBar; - - largest = b = tb = nil; - tw = width = nb = size = 0; - for(nb = 0; nb < nelem(s->bar); nb++) - for(b = s->bar[nb]; b; b=b->next) { - b->brush.rect.x = b->brush.rect.y = 0; - b->brush.rect.width = def.font.height & ~1; - if(b->text && strlen(b->text)) - b->brush.rect.width += textwidth(b->brush.font, b->text); - b->brush.rect.height = s->brect.height; - width += b->brush.rect.width; - } - /* Not enough room. Shrink bars until they all fit */ - if(width > s->brect.width) { - for(nb = 0; nb < nelem(s->bar); nb++) - for(b = s->bar[nb]; b; b=b->next) { - for(pb = &largest; *pb; pb = &(*pb)->smaller) - if((*pb)->brush.rect.width < b->brush.rect.width) - break; - b->smaller = *pb; - *pb = b; - } - for(tb = largest; tb; tb = tb->smaller) { - width -= tb->brush.rect.width; - tw += tb->brush.rect.width; - shrink = (s->brect.width - width) / (float)tw; - if(tb->smaller) - if(tb->brush.rect.width * shrink >= tb->smaller->brush.rect.width) - break; - } - if(tb) - for(b = largest; b != tb->smaller; b = b->smaller) - b->brush.rect.width = (int)(b->brush.rect.width * shrink); - width += tw * shrink; - tb = nil; - } - for(nb = 0; nb < nelem(s->bar); nb++) - for(b = s->bar[nb]; b; tb=b, b=b->next) { - if(b == s->bar[BarRight]) { - b->brush.align = EAST; - b->brush.rect.width += (s->brect.width - width); - }else - b->brush.align = CENTER; - if(tb) - b->brush.rect.x = tb->brush.rect.x + tb->brush.rect.width; - draw_label(&b->brush, b->text); - draw_border(&b->brush); - } -MapBar: - XCopyArea(blz.dpy, s->bbrush.drawable, s->barwin, s->bbrush.gc, 0, 0, - s->brect.width, s->brect.height, 0, 0); - XSync(blz.dpy, False); -} - -Bar * -bar_of_name(Bar *b_link, const char *name) { - static char buf[256]; - Bar *b; - - strncpy(buf, name, sizeof(buf)); - for(b = b_link; b; b = b->next) - if(!strncmp(b->name, name, sizeof(b->name))) break; - return b; -} diff --git a/client.c b/client.c @@ -1,958 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <stdlib.h> -#include <string.h> -#include <X11/Xatom.h> -#include "wmii.h" - -static void update_client_name(Client *c); - -static char Ebadcmd[] = "bad command", - Ebadvalue[] = "bad value"; - -enum { - ClientMask = - StructureNotifyMask - | PropertyChangeMask - | EnterWindowMask - | FocusChangeMask, - ButtonMask = - ButtonPressMask | ButtonReleaseMask -}; - -Client * -create_client(Window w, XWindowAttributes *wa) { - Client **t, *c; - XSetWindowAttributes fwa; - - c = emallocz(sizeof(Client)); - c->win = w; - c->rect.x = wa->x; - c->rect.y = wa->y; - c->border = wa->border_width; - c->rect.width = wa->width; - c->rect.height = wa->height; - - c->proto = win_proto(c->win); - prop_client(c, XA_WM_TRANSIENT_FOR); - prop_client(c, XA_WM_NORMAL_HINTS); - prop_client(c, XA_WM_HINTS); - prop_client(c, XA_WM_NAME); - - XSetWindowBorderWidth(blz.dpy, c->win, 0); - XAddToSaveSet(blz.dpy, c->win); - - fwa.override_redirect = True; - fwa.background_pixmap = ParentRelative; - fwa.backing_store = Always; - fwa.event_mask = - SubstructureRedirectMask - | SubstructureNotifyMask - | ExposureMask - | EnterWindowMask - | PointerMotionMask - | KeyPressMask - | ButtonPressMask - | ButtonReleaseMask; - c->framewin = XCreateWindow( - /* display */ blz.dpy, - /* parent */ blz.root, - /* x */ c->rect.x, - /* y */ c->rect.y, - /* width */ c->rect.width + 2 * def.border, - /* height */ c->rect.height + def.border + labelh(&def.font), - /* border */ 0, - /* depth */ DefaultDepth(blz.dpy, blz.screen), - /* class */ CopyFromParent, - /* visual */ DefaultVisual(blz.dpy, blz.screen), - /* valuemask */ CWOverrideRedirect | CWEventMask | CWBackPixmap | CWBackingStore, - /* attributes */&fwa - ); - - c->gc = XCreateGC(blz.dpy, c->framewin, 0, 0); - XSync(blz.dpy, False); - - for(t=&client ;; t=&(*t)->next) - if(!*t) { - c->next = *t; - *t = c; - break; - } - - write_event("CreateClient 0x%x\n", c->win); - return c; -} - -static int -dummy_error_handler(Display *dpy, XErrorEvent *error) { - return 0; -} - -void -destroy_client(Client *c) { - char *dummy; - Client **tc; - XEvent ev; - - XGrabServer(blz.dpy); - /* In case the client is already unmapped */ - XSetErrorHandler(dummy_error_handler); - - for(tc=&client; *tc; tc=&(*tc)->next) - if(*tc == c) { - *tc = c->next; - break; - } - - dummy = nil; - update_client_views(c, &dummy); - - unmap_client(c, WithdrawnState); - gravitate_client(c, True); - reparent_client(c, blz.root, c->rect.x, c->rect.y); - - XFreeGC(blz.dpy, c->gc); - XDestroyWindow(blz.dpy, c->framewin); - XSync(blz.dpy, False); - - XSetErrorHandler(wmii_error_handler); - XUngrabServer(blz.dpy); - flushevents(EnterWindowMask, False); - - while(XCheckMaskEvent(blz.dpy, StructureNotifyMask, &ev)) - if(ev.type != UnmapNotify || ev.xunmap.window != c->win) - dispatch_event(&ev); - - write_event("DestroyClient 0x%x\n", c->win); - free(c); -} - -void -manage_client(Client *c) { - XTextProperty tags = { 0 }; - Client *trans; - - XGetTextProperty(blz.dpy, c->win, &tags, atom[TagsAtom]); - - if((trans = client_of_win(c->trans))) - strncpy(c->tags, trans->tags, sizeof(c->tags)); - else if(tags.nitems) - strncpy(c->tags, (char *)tags.value, sizeof(c->tags)); - XFree(tags.value); - - gravitate_client(c, False); - reparent_client(c, c->framewin, def.border, labelh(&def.font)); - - if(!strlen(c->tags)) - apply_rules(c); - else - apply_tags(c, c->tags); - - if(!starting) - update_views(); - XSync(blz.dpy, False); - - if(c->sel->view == screen->sel) - focus(c, True); - flushevents(EnterWindowMask, False); -} - -Client * -sel_client() { - if(screen->sel && screen->sel->sel->sel) - return screen->sel->sel->sel->client; - return nil; -} - -Client * -client_of_win(Window w) { - Client *c; - for(c=client; c; c=c->next) - if(c->win == w) break; - return c; -} - -Frame * -frame_of_win(Window w) { - Client *c; - for(c=client; c; c=c->next) - if(c->framewin == w) break; - if(c) - return c->sel; - return nil; -} - -static void -update_client_name(Client *c) { - XTextProperty name; - XClassHint ch; - int n; - char **list = nil; - - name.nitems = 0; - c->name[0] = 0; - XGetTextProperty(blz.dpy, c->win, &name, atom[NetWMName]); - if(!name.nitems) - XGetWMName(blz.dpy, c->win, &name); - if(!name.nitems) - return; - if(name.encoding == XA_STRING) - strncpy(c->name, (char *)name.value, sizeof(c->name)); - else { - if(XmbTextPropertyToTextList(blz.dpy, &name, &list, &n) >= Success - && n > 0 && *list) - { - strncpy(c->name, *list, sizeof(c->name)); - XFreeStringList(list); - } - } - XFree(name.value); - if(XGetClassHint(blz.dpy, c->win, &ch)) { - snprintf(c->props, sizeof(c->props), - "%s:%s:%s", - str_nil(ch.res_class), - str_nil(ch.res_name), - c->name); - if(ch.res_class) - XFree(ch.res_class); - if(ch.res_name) - XFree(ch.res_name); - } -} - -void -update_client_grab(Client *c) { - Frame *f; - f = c->sel; - if((f->client != sel_client()) - || (f->area->floating && f != f->area->stack)) { - if(verbose) - fprintf(stderr, "update_client_grab(%p) AnyButton => %s\n", c, str_nil(c->name)); - grab_button(c->framewin, AnyButton, AnyModifier); - }else { - if(verbose) - fprintf(stderr, "update_client_grab(%p) def.mod => %s\n", c, str_nil(c->name)); - XUngrabButton(blz.dpy, AnyButton, AnyModifier, c->framewin); - grab_button(c->framewin, Button1, def.mod); - grab_button(c->framewin, Button3, def.mod); - } -} - -void -set_client_state(Client * c, int state) { - long data[] = { state, None }; - XChangeProperty( - /* display */ blz.dpy, - /* parent */ c->win, - /* property */ atom[WMState], - /* type */ atom[WMState], - /* format */ 32, - /* mode */ PropModeReplace, - /* data */ (uchar *) data, - /* npositions */2 - ); -} - -void -map_client(Client *c) { - if(!c->mapped) { - XSelectInput(blz.dpy, c->win, ClientMask & ~StructureNotifyMask); - XMapWindow(blz.dpy, c->win); - XSelectInput(blz.dpy, c->win, ClientMask); - set_client_state(c, NormalState); - c->mapped = 1; - } -} - -void -unmap_client(Client *c, int state) { - if(c->mapped) { - c->unmapped++; - XSelectInput(blz.dpy, c->win, ClientMask & ~StructureNotifyMask); - XUnmapWindow(blz.dpy, c->win); - XSelectInput(blz.dpy, c->win, ClientMask); - set_client_state(c, state); - c->mapped = 0; - } -} - -void -map_frame(Client *c) { - if(!c->frame_mapped) { - XMapWindow(blz.dpy, c->framewin); - c->frame_mapped = True; - } -} - -void -unmap_frame(Client *c) { - if(c->frame_mapped) { - XUnmapWindow(blz.dpy, c->framewin); - c->frame_mapped = False; - } -} - -void -reparent_client(Client *c, Window w, int x, int y) { - XSelectInput(blz.dpy, c->win, ClientMask & ~StructureNotifyMask); - XReparentWindow(blz.dpy, c->win, w, x, y); - XSelectInput(blz.dpy, c->win, ClientMask); -} - -void -set_cursor(Client *c, Cursor cur) { - XSetWindowAttributes wa; - - if(c->cursor != cur) { - c->cursor = cur; - wa.cursor = cur; - XChangeWindowAttributes(blz.dpy, c->framewin, CWCursor, &wa); - } -} - -void -configure_client(Client *c) { - XConfigureEvent e; - Frame *f; - - f = c->sel; - if(!f) - return; - - e.type = ConfigureNotify; - e.event = c->win; - e.window = c->win; - e.x = f->crect.x + f->rect.x - c->border; - e.y = f->crect.y + f->rect.y - c->border; - e.width = f->crect.width; - e.height = f->crect.height; - e.border_width = c->border; - e.above = None; - e.override_redirect = False; - XSendEvent(blz.dpy, c->win, False, - StructureNotifyMask, (XEvent *) & e); - XSync(blz.dpy, False); -} - -static void -send_client_message(Window w, Atom a, long value) { - XEvent e; - - e.type = ClientMessage; - e.xclient.window = w; - e.xclient.message_type = a; - e.xclient.format = 32; - e.xclient.data.l[0] = value; - e.xclient.data.l[1] = CurrentTime; - XSendEvent(blz.dpy, w, False, NoEventMask, &e); - XSync(blz.dpy, False); -} - -void -kill_client(Client * c) { - if(c->proto & WM_PROTOCOL_DELWIN) - send_client_message(c->win, atom[WMProtocols], atom[WMDelete]); - else - XKillClient(blz.dpy, c->win); -} - -static void -set_urgent(Client *c, Bool urgent, Bool write) { - XWMHints *wmh; - char *cwrite, *cnot; - Frame *f, *ff; - Area *a; - - cwrite = "Client"; - if(write) - cwrite = "Manager"; - cnot = "Not"; - if(urgent) - cnot = ""; - - if(urgent != c->urgent) { - write_event("%sUrgent 0x%x %s\n", cnot, c->win, cwrite); - c->urgent = urgent; - if(c->sel) { - if(c->sel->view == screen->sel) - draw_frame(c->sel); - if(!urgent || c->sel->view != screen->sel) - for(f=c->frame; f; f=f->cnext) { - for(a=f->view->area; a; a=a->next) - for(ff=a->frame; ff; ff=ff->anext) - if(ff->client->urgent) break; - if(!ff) - write_event("%sUrgentTag %s %s\n", cnot, cwrite, f->view->name); - } - } - } - - if(write) { - wmh = XGetWMHints(blz.dpy, c->win); - if(wmh) { - if(urgent) - wmh->flags |= XUrgencyHint; - else - wmh->flags &= ~XUrgencyHint; - XSetWMHints(blz.dpy, c->win, wmh); - XFree(wmh); - } - } -} - -void -prop_client(Client *c, Atom a) { - XWMHints *wmh; - long msize; - - if(a == atom[WMProtocols]) - c->proto = win_proto(c->win); - else if(a== atom[NetWMName]) { -wmname: - update_client_name(c); - if(c->frame) - draw_frame(c->sel); - }else switch (a) { - case XA_WM_TRANSIENT_FOR: - XGetTransientForHint(blz.dpy, c->win, &c->trans); - break; - case XA_WM_NORMAL_HINTS: - if(!XGetWMNormalHints(blz.dpy, c->win, &c->size, &msize) || !c->size.flags) - c->size.flags = PSize; - c->fixedsize = False; - if((c->size.flags & PMinSize) && (c->size.flags & PMaxSize) - &&(c->size.min_width == c->size.max_width) - &&(c->size.min_height == c->size.max_height)) - c->fixedsize = True; - break; - case XA_WM_HINTS: - wmh = XGetWMHints(blz.dpy, c->win); - if(wmh) { - set_urgent(c, (wmh->flags & XUrgencyHint) != 0, False); - XFree(wmh); - } - break; - case XA_WM_NAME: - goto wmname; - } -} - -void -gravitate_client(Client *c, Bool invert) { - int dx, dy; - int gravity; - - gravity = NorthWestGravity; - if(c->size.flags & PWinGravity) { - gravity = c->size.win_gravity; - } - - dy = 0; - switch (gravity) { - case StaticGravity: - case NorthWestGravity: - case NorthGravity: - case NorthEastGravity: - dy = labelh(&def.font); - break; - case EastGravity: - case CenterGravity: - case WestGravity: - dy = -(c->rect.height / 2) + labelh(&def.font); - break; - case SouthEastGravity: - case SouthGravity: - case SouthWestGravity: - dy = -c->rect.height; - break; - default: - break; - } - - dx = 0; - switch (gravity) { - case StaticGravity: - case NorthWestGravity: - case WestGravity: - case SouthWestGravity: - dx = def.border; - break; - case NorthGravity: - case CenterGravity: - case SouthGravity: - dx = -(c->rect.width / 2) + def.border; - break; - case NorthEastGravity: - case EastGravity: - case SouthEastGravity: - dx = -(c->rect.width + def.border); - break; - default: - break; - } - - if(invert) { - dx = -dx; - dy = -dy; - } - c->rect.x += dx; - c->rect.y += dy; -} - -void -apply_sizehints(Client *c, XRectangle *r, Bool floating, Bool frame, BlitzAlign sticky) { - XSizeHints *s; - XRectangle orig; - uint bw, bh; - - s = &c->size; - orig = *r; - if(frame) - frame2client(r); - bw = 0; - bh = 0; - - if(s->flags & PMinSize) { - bw = s->min_width; - bh = s->min_height; - if(floating) { - if(r->width < s->min_width) - r->width = s->min_width; - if(r->height < s->min_height) - r->height = s->min_height; - } - } - if(s->flags & PMaxSize) { - if(r->width > s->max_width) - r->width = s->max_width; - if(r->height > s->max_height) - r->height = s->max_height; - } - - if(s->flags & PBaseSize) { - bw = s->base_width; - bh = s->base_height; - } - - if(s->flags & PResizeInc) { - if(s->width_inc > 0) - r->width -= (r->width - bw) % s->width_inc; - if(s->height_inc > 0) - r->height -= (r->height - bh) % s->height_inc; - } - - if((s->flags & (PBaseSize|PMinSize)) == PMinSize) { - bw = 0; - bh = 0; - } - - if(s->flags & PAspect) { - double min, max, initial; - - min = (double)s->min_aspect.x / s->min_aspect.y; - max = (double)s->max_aspect.x / s->max_aspect.y; - initial = (double)(r->width - bw) / (r->height - bh); - if(initial < min) - r->height = bh + (r->width - bw) / min; - if(initial > max) - r->width = bw + (r->height - bh) * max; - } - - if(frame) - client2frame(r); - - if(!(s->flags & PMinSize) || !floating) { - if(r->width > orig.width) - r->width = orig.width; - if(r->height > orig.height) - r->height = orig.height; - } - - if((sticky & (EAST|WEST)) == EAST) - r->x = r_east(&orig) - r->width; - if((sticky & (NORTH|SOUTH)) == SOUTH) - r->y = r_south(&orig) - r->height; -} - -void -focus(Client *c, Bool restack) { - View *v; - Frame *f; - - f = c->sel; - if(!f) - return; - - v = f->area->view; - if(v != screen->sel) - focus_view(screen, v); - focus_frame(c->sel, restack); -} - -void -focus_client(Client *c) { - flushevents(FocusChangeMask, True); - - if(verbose) - fprintf(stderr, "focus_client(%p) => %s\n", c, (c ? c->name : nil)); - if(screen->focus != c) { - if(c && verbose) - fprintf(stderr, "\t%s => %s\n", (screen->focus ? screen->focus->name : "<nil>"), - (c ? c->name : "<nil>")); - if(c) { - XSetInputFocus(blz.dpy, c->win, RevertToParent, CurrentTime); - update_client_grab(c); - }else - XSetInputFocus(blz.dpy, screen->barwin, RevertToParent, CurrentTime); - } - - flushevents(FocusChangeMask, True); -} - -void -resize_client(Client *c, XRectangle *r) { - Frame *f; - - f = c->sel; - resize_frame(f, r); - - if(f->area->view != screen->sel) { - unmap_client(c, IconicState); - unmap_frame(c); - return; - } - - c->rect = f->crect; - c->rect.x += f->rect.x; - c->rect.y += f->rect.y; - if((f->area->mode == Colmax) - && (f->area->sel != f)) { - unmap_frame(c); - unmap_client(c, IconicState); - }else if(f->collapsed) { - XMoveResizeWindow(blz.dpy, c->framewin, - f->rect.x, f->rect.y, - f->rect.width, f->rect.height); - map_frame(c); - unmap_client(c, IconicState); - }else { - XMoveResizeWindow(blz.dpy, c->win, - f->crect.x, f->crect.y, - f->crect.width, f->crect.height); - map_client(c); - XMoveResizeWindow(blz.dpy, c->framewin, - f->rect.x, f->rect.y, - f->rect.width, f->rect.height); - map_frame(c); - configure_client(c); - } - - flushevents(FocusChangeMask|ExposureMask, True); -} - -void -newcol_client(Client *c, char *arg) { - Frame *f = c->sel; - Area *to, *a = f->area; - View *v = a->view; - - if(a->floating) - return; - if(!f->anext && f == a->frame) - return; - if(!strncmp(arg, "prev", 5)) { - for(to=v->area; to; to=to->next) - if(to->next == a) break; - to = new_column(v, to, 0); - send_to_area(to, f); - } - else if(!strncmp(arg, "next", 5)) { - to = new_column(v, a, 0); - send_to_area(to, f); - } - else - return; - flushevents(EnterWindowMask, False); -} - -void -move_client(Client *c, char *arg) { - Frame *f = c->sel; - XRectangle new = f->rect; - int x, y; - - if(sscanf(arg, "%d %d", &x, &y) != 2) - return; - new.x += x; - new.y += y; - if(!f->area->floating) - resize_column(f->client, &new); - else - resize_client(f->client, &new); -} - -void -size_client(Client *c, char *arg) { - Frame *f = c->sel; - XRectangle new = f->rect; - int w, h; - - if(sscanf(arg, "%d %d", &w, &h) != 2) - return; - new.width += w; - new.height += h; - if(!f->area->floating) - resize_column(f->client, &new); - else - resize_client(f->client, &new); -} - -char * -send_client(Frame *f, char *arg, Bool swap) { - Area *to, *a; - Client *c; - Frame *tf; - View *v; - Bool before; - int j; - - a = f->area; - v = a->view; - c = f->client; - if(!strncmp(arg, "toggle", 7)) { - if(!a->floating) - to = v->area; - else if(c->revert && !c->revert->floating) - to = c->revert; - else - to = v->area->next; - goto send_area; - }else if(!a->floating) { - if(!strncmp(arg, "left", 5)) { - if(a->floating) - return Ebadvalue; - for(to=v->area->next; to; to=to->next) - if(a == to->next) break; - if(!to && !swap && (f->anext || f != a->frame)) - to=new_column(v, v->area, 0); - goto send_area; - } - else if(!strncmp(arg, "right", 5)) { - if(a->floating) - return Ebadvalue; - to = a->next; - if(!to && !swap && (f->anext || f != a->frame)) - to = new_column(v, a, 0); - goto send_area; - } - else if(!strncmp(arg, "up", 3)) { - for(tf=a->frame; tf; tf=tf->anext) - if(tf->anext == f) break; - before = True; - goto send_frame; - } - else if(!strncmp(arg, "down", 5)) { - tf = f->anext; - before = False; - goto send_frame; - } - else { - if(sscanf(arg, "%d", &j) != 1) - return Ebadvalue; - for(to=v->area; to; to=to->next) - if(!--j) break; - goto send_area; - } - } - return Ebadvalue; - -send_frame: - if(!tf) - return Ebadvalue; - if(!swap) { - remove_frame(f); - insert_frame(tf, f, before); - }else - swap_frames(f, tf); - arrange_column(a, False); - - flushevents(EnterWindowMask, False); - focus_frame(f, True); - update_views(); - return nil; - -send_area: - if(!to) - return Ebadvalue; - if(!swap) - send_to_area(to, f); - else if(to->sel) - swap_frames(f, to->sel); - - flushevents(EnterWindowMask, False); - focus_frame(f, True); - update_views(); - return nil; -} - -void -update_client_views(Client *c, char **tags) { - Frame **fp, *f; - int cmp; - - fp = &c->frame; - while(*fp || *tags) { - while(*fp) { - if(*tags) { - cmp = strcmp((*fp)->view->name, *tags); - if(cmp >= 0) - break; - } - - f = *fp; - detach_from_area(f); - *fp = f->cnext; - if(c->sel == f) - c->sel = *fp; - free(f); - } - if(*tags) { - if(!*fp || cmp > 0) { - f = create_frame(c, get_view(*tags)); - if(f->view == screen->sel || !c->sel) - c->sel = f; - attach_to_view(f->view, f); - f->cnext = *fp; - *fp = f; - } - if(*fp) fp=&(*fp)->cnext; - tags++; - } - } - update_views(); -} - -static int -compare_tags(const void *a, const void *b) { - return strcmp(*(char **)a, *(char **)b); -} - -void -apply_tags(Client *c, const char *tags) { - uint i, j, k, n; - Bool add; - char buf[512], last; - char *toks[32], *cur; - - buf[0] = 0; - for(n = 0; tags[n]; n++) - if(tags[n] != ' ' && tags[n] != '\t') break; - if(tags[n] == '+' || tags[n] == '-') - strncpy(buf, c->tags, sizeof(c->tags)); - strlcat(buf, &tags[n], sizeof(buf)); - trim(buf, " \t/"); - - n = 0; - j = 0; - add = True; - if(buf[0] == '+') - n++; - else if(buf[0] == '-') { - n++; - add = False; - } - while(buf[n] && n < sizeof(buf) && j < 32) { - for(i = n; i < sizeof(buf) - 1; i++) - if(buf[i] == '+' - || buf[i] == '-' - || buf[i] == '\0') - break; - last = buf[i]; - buf[i] = '\0'; - - cur = nil; - if(!strncmp(&buf[n], "~", 2)) - c->floating = add; - else if(!strncmp(&buf[n], "!", 2)) - cur = view ? screen->sel->name : "nil"; - else if(strncmp(&buf[n], "sel", 4) - && strncmp(&buf[n], ".", 2) - && strncmp(&buf[n], "..", 3)) - cur = &buf[n]; - - n = i + 1; - if(cur) { - if(add) - toks[j++] = cur; - else { - for(i = 0, k = 0; i < j; i++) - if(strcmp(toks[i], cur)) - toks[k++] = toks[i]; - j = k; - } - } - - switch(last) { - case '+': - add = True; - break; - case '-': - add = False; - break; - case '\0': - buf[n] = '\0'; - break; - } - } - - c->tags[0] = '\0'; - if(!j) - return; - qsort(toks, j, sizeof(char *), compare_tags); - - for(i=0, n=0; i < j; i++) - if(!n || strcmp(toks[i], toks[n-1])) { - if(i) - strlcat(c->tags, "+", sizeof(c->tags)); - strlcat(c->tags, toks[i], sizeof(c->tags)); - toks[n++] = toks[i]; - } - toks[n] = nil; - - update_client_views(c, toks); - XChangeProperty(blz.dpy, c->win, atom[TagsAtom], XA_STRING, 8, - PropModeReplace, (uchar *)c->tags, strlen(c->tags)); -} - -void -apply_rules(Client *c) { - Rule *r; - regmatch_t rm; - - if(strlen(c->tags)) - return; - if(def.tagrules.string) - for(r=def.tagrules.rule; r; r=r->next) - if(!regexec(&r->regex, c->props, 1, &rm, 0)) { - apply_tags(c, r->value); - if(strlen(c->tags) && strcmp(c->tags, "nil")) - break; - } - if(!strlen(c->tags)) - apply_tags(c, "nil"); -} - -char * -message_client(Client *c, char *message) { - if(!strncmp(message, "kill", 5)) - kill_client(c); - else if(!strncmp(message, "Urgent", 7)) - set_urgent(c, True, True); - else if(!strncmp(message, "NotUrgent", 10)) - set_urgent(c, False, True); - else - return Ebadcmd; - return nil; -} diff --git a/cmd/Makefile b/cmd/Makefile @@ -0,0 +1,26 @@ +ROOT=.. +include ${ROOT}/mk/hdr.mk + +DIRS = wmii +TARG = wmii9menu \ + wmii9rc \ + wmiiloop \ + wmiir \ + wmiistartrc + +OFILES = util.o +FILTER = sed "s|CONFPREFIX|${CONFPREFIX}|g; \ + s|CONFVERSION|${CONFVERSION}|g; \ + s|P9PATHS|${P9PATHS}|g; \ + s|AWKPATH|${AWKPATH}|g" + +include ${ROOT}/mk/many.mk +include ${ROOT}/mk/dir.mk + +OWMIIR=${OFILES} ${LIBIXP} +wmiir.O: ${OWMIIR} + ${LINK} $@ $*.o ${OWMIIR} + +EXCFLAGS=${INCX11} +wmii9menu.O: + ${LINK} $@ $*.o ${LIBX11} diff --git a/cmd/util.c b/cmd/util.c @@ -0,0 +1,148 @@ +/* Written by Kris Maglione <fbsdaemon at gmail dot com> */ +/* Public domain */ +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <util.h> + +void +fatal(const char *fmt, ...) { + va_list ap; + int err; + + err = errno; + fprintf(stderr, "%s: fatal: ", argv0); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if(fmt[strlen(fmt)-1] == ':') + fprintf(stderr, " %s\n", strerror(err)); + else + fprintf(stderr, "\n"); + + exit(1); +} + +/* Can't malloc */ +void +mfatal(char *name, uint size) { + const char + couldnot[] = ": fatal: Could not ", + paren[] = "() ", + bytes[] = " bytes\n"; + char sizestr[8]; + int i; + + i = sizeof(sizestr); + do { + sizestr[--i] = '0' + (size%10); + size /= 10; + } while(size > 0); + + write(1, argv0, strlen(argv0)-1); + write(1, couldnot, sizeof(couldnot)-1); + write(1, name, strlen(name)); + write(1, paren, sizeof(paren)-1); + write(1, sizestr+i, sizeof(sizestr)-i); + write(1, bytes, sizeof(bytes)-1); + + exit(1); +} + +void * +emalloc(uint size) { + void *ret = malloc(size); + if(!ret) + mfatal("malloc", size); + return ret; +} + +void * +emallocz(uint size) { + void *ret = emalloc(size); + memset(ret, 0, size); + return ret; +} + +void * +erealloc(void *ptr, uint size) { + void *ret = realloc(ptr, size); + if(!ret) + mfatal("realloc", size); + return ret; +} + +char * +estrdup(const char *str) { + void *ret = strdup(str); + if(!ret) + mfatal("strdup", strlen(str)); + return ret; +} + +uint +tokenize(char *res[], uint reslen, char *str, char delim) { + char *s; + uint i; + + i = 0; + s = str; + while(i < reslen && *s) { + while(*s == delim) + *(s++) = '\0'; + if(*s) + res[i++] = s; + while(*s && *s != delim) + s++; + } + return i; +} + +int +max(int a, int b) { + if(a > b) + return a; + return b; +} + +int +min(int a, int b) { + if(a < b) + return a; + return b; +} + +char * +str_nil(char *s) { + if(s) + return s; + return "<nil>"; +} + +uint +strlcat(char *dst, const char *src, uint size) { + const char *s; + char *d; + int n, len; + + d = dst; + s = src; + n = size; + while(n-- > 0 && *d != '\0') + d++; + len = n; + + while(*s != '\0') { + if(n-- > 0) + *d++ = *s; + s++; + } + if(len > 0) + *d = '\0'; + return size - n - 1; +} diff --git a/cmd/wmii/Makefile b/cmd/wmii/Makefile @@ -0,0 +1,26 @@ +ROOT= ../.. +include ${ROOT}/mk/hdr.mk + +TARG = wmii + +LIB = ${LIBIXP} +EXLDFLAGS = -lm ${LIBX11} +EXCFLAGS = ${INCX11} +OBJ = area \ + bar \ + client \ + column \ + draw \ + event \ + frame \ + fs \ + geom \ + key \ + main \ + mouse \ + rule \ + printevent\ + view \ + ../util + +include ${ROOT}/mk/one.mk diff --git a/cmd/wmii/area.c b/cmd/wmii/area.c @@ -0,0 +1,438 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include "wmii.h" + +static void place_frame(Frame *f); + +Client * +sel_client_of_area(Area *a) { + if(a && a->sel) + return a->sel->client; + return nil; +} + +Area * +create_area(View *v, Area *pos, uint w) { + static ushort id = 1; + uint area_num, col_num, i; + uint min_width; + Area *a, **p; + + min_width = screen->rect.width/NCOL; + p = pos ? &pos->next : &v->area; + + area_num = 0; + i = 0; + for(a = v->area; a != *p; a = a->next) + area_num++, i++; + for(; a; a = a->next) area_num++; + + col_num = max((area_num - 1), 0); + if(w == 0) { + if(col_num) { + w = newcolw_of_view(v); + if (w == 0) + w = screen->rect.width / (col_num + 1); + } + else w = screen->rect.width; + } + if(w < min_width) + w = min_width; + if(col_num && (col_num * min_width + w) > screen->rect.width) + return nil; + if(pos) + scale_view(v, screen->rect.width - w); + + a = emallocz(sizeof(Area)); + a->view = v; + a->id = id++; + a->rect = screen->rect; + a->rect.height = screen->rect.height - screen->brect.height; + a->mode = def.colmode; + a->rect.width = w; + a->frame = nil; + a->sel = nil; + a->next = *p; + *p = a; + + if(a == v->area) + a->floating = True; + if((!v->sel) || + (v->sel->floating && v->area->next == a && a->next == nil)) + focus_area(a); + + if(i) + write_event("CreateColumn %d\n", i); + return a; +} + +void +destroy_area(Area *a) { + Client *c; + Area *ta; + View *v; + uint i; + + v = a->view; + + if(a->frame) + fatal("destroying non-empty area"); + if(v->revert == a) + v->revert = nil; + for(c=client; c; c=c->next) + if(c->revert == a) + c->revert = nil; + + i = 1; + for(ta=v->area; ta; ta=ta->next) + if(ta->next == a) break; + else i++; + if(ta) { + ta->next = a->next; + if(ta->floating && ta->next) + ta = ta->next; + if(v->sel == a) + focus_area(ta); + } + if(i) write_event("DestroyColumn %d\n", i); + free(a); +} + +void +send_to_area(Area *to, Frame *f) { + Area *from; + assert(to->view == f->view); + from = f->area; + if(to->floating != from->floating) { + XRectangle temp = f->revert; + f->revert = f->rect; + f->rect = temp; + } + f->client->revert = from; + detach_from_area(f); + attach_to_area(to, f, True); +} + +void +attach_to_area(Area *a, Frame *f, Bool send) { + uint h, n_frame; + Frame *ft; + Client *c; + View *v; + + v = a->view; + c = f->client; + h = 0; + + f->area = a; + + n_frame = 0; + for(ft=a->frame; ft; ft=ft->anext) + n_frame++; + if(n_frame == 0) + n_frame = 1; + + c->floating = a->floating; + if(!a->floating) + f->rect.height = a->rect.height / n_frame; + + insert_frame(a->sel, f, False); + + if(a->floating) + place_frame(f); + + focus_frame(f, False); + resize_frame(f, &f->rect); + restack_view(a->view); + if(!a->floating) + arrange_column(a, False); + else + resize_frame(f, &f->rect); + + update_client_grab(f->client); + if(a->frame) + assert(a->sel); +} + +void +detach_from_area(Frame *f) { + Frame *pr; + Client *c; + Area *a; + View *v; + Area *ta; + uint i; + + a = f->area; + v = a->view; + c = f->client; + + for(pr = a->frame; pr; pr = pr->anext) + if(pr->anext == f) break; + remove_frame(f); + + if(a->sel == f) { + if(!pr) + pr = a->frame; + if((a->view->sel == a) && (pr)) + focus_frame(pr, False); + else + a->sel = pr; + } + + if(!a->floating) { + if(a->frame) + arrange_column(a, False); + else { + i = 0; + for(ta=v->area; ta && ta != a; ta=ta->next) + i++; + if(v->area->next->next) + destroy_area(a); + else if(!a->frame && v->area->frame) + /* focus floating area if it contains something */ + focus_area(v->area); + arrange_view(v); + } + } + else if(!a->frame) { + if(c->trans) { + /* focus area of transient, if possible */ + Client *cl = client_of_win(c->trans); + if(cl && cl->frame) { + a = cl->sel->area; + if(a->view == v) + focus_area(a); + } + } + else if(v->area->next->frame) + focus_area(v->area->next); + }else + assert(a->sel); +} + +static void +bit_twiddle(uint *field, uint width, uint x, uint y, Bool set) { + enum { devisor = sizeof(uint) * 8 }; + uint bx, mask; + + bx = x / devisor; + mask = 1 << x % devisor; + if(set) + field[y*width + bx] |= mask; + else + field[y*width + bx] &= ~mask; +} + +static Bool +bit_get(uint *field, uint width, uint x, uint y) { + enum { devisor = sizeof(uint) * 8 }; + uint bx, mask; + + bx = x / devisor; + mask = 1 << x % devisor; + + return (field[y*width + bx] & mask) != 0; +} + +static void +place_frame(Frame *f) { + enum { devisor = sizeof(uint) * 8 }; + enum { dx = 8, dy = 8 }; + static uint mwidth, mx, my; + static uint *field = nil; + BlitzAlign align; + XPoint p1 = {0, 0}; + XPoint p2 = {0, 0}; + XRectangle *rects; + Frame *fr; + Client *c; + Area *a; + Bool fit; + uint i, j, x, y, cx, cy, maxx, maxy, diff, num; + int snap; + + snap = screen->rect.height / 66; + num = 0; + fit = False; + align = CENTER; + + a = f->area; + c = f->client; + + if(c->trans) + return; + if(c->rect.width >= a->rect.width + || c->rect.height >= a->rect.height + || c->size.flags & USPosition + || c->size.flags & PPosition) + return; + if(!field) { + mx = screen->rect.width / dx; + my = screen->rect.height / dy; + mwidth = ceil((float)mx / devisor); + field = emallocz(sizeof(uint) * mwidth * my); + } + memset(field, ~0, (sizeof(uint) * mwidth * my)); + for(fr=a->frame; fr; fr=fr->anext) { + if(fr == f) { + cx = f->rect.width / dx; + cy = f->rect.height / dy; + continue; + } + if(fr->rect.x < 0) + x = 0; + else + x = fr->rect.x / dx; + if(fr->rect.y < 0) + y = 0; + else + y = fr->rect.y / dy; + maxx = r_east(&fr->rect) / dx; + maxy = r_south(&fr->rect) / dy; + for(j = y; j < my && j < maxy; j++) + for(i = x; i < mx && i < maxx; i++) + bit_twiddle(field, mwidth, i, j, False); + } + for(y = 0; y < my; y++) + for(x = 0; x < mx; x++) { + if(bit_get(field, mwidth, x, y)) { + for(i = x; (i < mx) && bit_get(field, mwidth, i, y); i++); + for(j = y; (j < my) && bit_get(field, mwidth, x, j); j++); + if(((i - x) * (j - y) > (p2.x - p1.x) * (p2.y - p1.y)) + && (i - x > cx) && (j - y > cy)) + { + fit = True; + p1.x = x; + p1.y = y; + p2.x = i; + p2.y = j; + } + } + } + if(fit) { + p1.x *= dx; + p1.y *= dy; + } + if(fit && (p1.x + f->rect.width < r_south(&a->rect))) + f->rect.x = p1.x; + else { + diff = a->rect.width - f->rect.width; + f->rect.x = a->rect.x + (random() % (diff ? diff : 1)); + } + if(fit && (p1.y + f->rect.height < (r_south(&a->rect)))) + f->rect.y = p1.y; + else { + diff = a->rect.height - f->rect.height; + f->rect.y = a->rect.y + (random() % (diff ? diff : 1)); + } + + rects = rects_of_view(a->view, &num, nil); + snap_rect(rects, num, &f->rect, &align, snap); + if(rects) + free(rects); +} + +void +focus_area(Area *a) { + Frame *f; + View *v; + Area *old_a; + int i; + + v = a->view; + f = a->sel; + old_a = v->sel; + + v->sel = a; + + if((old_a) + && (a->floating != old_a->floating)) + v->revert = old_a; + + if(v != screen->sel) + return; + + if(f) + focus_client(f->client); + else + focus_client(nil); + + if(a != old_a) { + i = 0; + for(a = v->area; a != v->sel; a = a->next) + i++; + if(i) + write_event("ColumnFocus %d\n", i); + else + write_event("FocusFloating\n"); + if(a->frame) + write_event("ClientFocus 0x%x\n", a->sel->client->win); + } +} + +char * +select_area(Area *a, char *arg) { + Area *new; + uint i; + Frame *p, *f; + View *v; + static char Ebadvalue[] = "bad value"; + + v = a->view; + f = a->sel; + if(!strncmp(arg, "toggle", 7)) { + if(!a->floating) + new = v->area; + else if(v->revert) + new = v->revert; + else + new = v->area->next; + } else if(!strncmp(arg, "left", 5)) { + if(a->floating) + return Ebadvalue; + for(new=v->area->next; new->next; new=new->next) + if(new->next == a) break; + } else if(!strncmp(arg, "right", 5)) { + if(a->floating) + return Ebadvalue; + new = a->next ? a->next : v->area->next; + } + else if(!strncmp(arg, "up", 3)) { + if(!f) + return Ebadvalue; + for(p=a->frame; p->anext; p=p->anext) + if(p->anext == f) break; + goto focus_frame; + } + else if(!strncmp(arg, "down", 5)) { + if(!f) + return Ebadvalue; + p = f->anext ? f->anext : a->frame; + goto focus_frame; + } + else if(!strncmp(arg, "~", 2)) { + new = v->area; + } + else { + if(sscanf(arg, "%u", &i) != 1 || i == 0) + return Ebadvalue; + for(new=v->area->next; new->next; new=new->next) + if(!--i) break; + } + focus_area(new); + return nil; + +focus_frame: + focus_frame(p, False); + frame_to_top(p); + if(v == screen->sel) + restack_view(v); + return nil; +} diff --git a/cmd/wmii/bar.c b/cmd/wmii/bar.c @@ -0,0 +1,134 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <string.h> +#include "wmii.h" + +Bar *free_bars = nil; + +Bar * +create_bar(Bar **b_link, char *name) { + static uint id = 1; + Bar **i, *b = bar_of_name(*b_link, name);; + + if(b) + return b; + if(free_bars) { + b = free_bars; + free_bars = b->next; + memset(b, 0, sizeof(*b)); + } + else + b = emallocz(sizeof(Bar)); + b->id = id++; + strncpy(b->name, name, sizeof(b->name)); + b->brush = screen->bbrush; + b->brush.color = def.normcolor; + for(i = b_link; *i; i = &(*i)->next) + if(strcmp((*i)->name, name) >= 0) + break; + b->next = *i; + *i = b; + + return b; +} + +void +destroy_bar(Bar **b_link, Bar *b) { + Bar **p; + + for(p = b_link; *p; p = &(*p)->next) + if(*p == b) break; + *p = b->next; + b->next = free_bars; + free_bars = b; +} + +void +resize_bar(WMScreen *s) { + View *v; + + s->brect = s->rect; + s->brect.height = labelh(&def.font); + s->brect.y = s->rect.height - s->brect.height; + XMoveResizeWindow(blz.dpy, s->barwin, s->brect.x, s->brect.y, s->brect.width, s->brect.height); + XSync(blz.dpy, False); + draw_bar(s); + for(v = view; v; v = v->next) + arrange_view(v); +} + +void +draw_bar(WMScreen *s) { + uint width, tw, nb, size; + float shrink; + Bar *b, *tb, *largest, **pb; + + draw_tile(&s->bbrush); + if(!s->bar[BarLeft] && !s->bar[BarRight]) + goto MapBar; + + largest = b = tb = nil; + tw = width = nb = size = 0; + for(nb = 0; nb < nelem(s->bar); nb++) + for(b = s->bar[nb]; b; b=b->next) { + b->brush.rect.x = b->brush.rect.y = 0; + b->brush.rect.width = def.font.height & ~1; + if(b->text && strlen(b->text)) + b->brush.rect.width += textwidth(b->brush.font, b->text); + b->brush.rect.height = s->brect.height; + width += b->brush.rect.width; + } + /* Not enough room. Shrink bars until they all fit */ + if(width > s->brect.width) { + for(nb = 0; nb < nelem(s->bar); nb++) + for(b = s->bar[nb]; b; b=b->next) { + for(pb = &largest; *pb; pb = &(*pb)->smaller) + if((*pb)->brush.rect.width < b->brush.rect.width) + break; + b->smaller = *pb; + *pb = b; + } + for(tb = largest; tb; tb = tb->smaller) { + width -= tb->brush.rect.width; + tw += tb->brush.rect.width; + shrink = (s->brect.width - width) / (float)tw; + if(tb->smaller) + if(tb->brush.rect.width * shrink >= tb->smaller->brush.rect.width) + break; + } + if(tb) + for(b = largest; b != tb->smaller; b = b->smaller) + b->brush.rect.width = (int)(b->brush.rect.width * shrink); + width += tw * shrink; + tb = nil; + } + for(nb = 0; nb < nelem(s->bar); nb++) + for(b = s->bar[nb]; b; tb=b, b=b->next) { + if(b == s->bar[BarRight]) { + b->brush.align = EAST; + b->brush.rect.width += (s->brect.width - width); + }else + b->brush.align = CENTER; + if(tb) + b->brush.rect.x = tb->brush.rect.x + tb->brush.rect.width; + draw_label(&b->brush, b->text); + draw_border(&b->brush); + } +MapBar: + XCopyArea(blz.dpy, s->bbrush.drawable, s->barwin, s->bbrush.gc, 0, 0, + s->brect.width, s->brect.height, 0, 0); + XSync(blz.dpy, False); +} + +Bar * +bar_of_name(Bar *b_link, const char *name) { + static char buf[256]; + Bar *b; + + strncpy(buf, name, sizeof(buf)); + for(b = b_link; b; b = b->next) + if(!strncmp(b->name, name, sizeof(b->name))) break; + return b; +} diff --git a/cmd/wmii/client.c b/cmd/wmii/client.c @@ -0,0 +1,958 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <stdlib.h> +#include <string.h> +#include <X11/Xatom.h> +#include "wmii.h" + +static void update_client_name(Client *c); + +static char Ebadcmd[] = "bad command", + Ebadvalue[] = "bad value"; + +enum { + ClientMask = + StructureNotifyMask + | PropertyChangeMask + | EnterWindowMask + | FocusChangeMask, + ButtonMask = + ButtonPressMask | ButtonReleaseMask +}; + +Client * +create_client(Window w, XWindowAttributes *wa) { + Client **t, *c; + XSetWindowAttributes fwa; + + c = emallocz(sizeof(Client)); + c->win = w; + c->rect.x = wa->x; + c->rect.y = wa->y; + c->border = wa->border_width; + c->rect.width = wa->width; + c->rect.height = wa->height; + + c->proto = win_proto(c->win); + prop_client(c, XA_WM_TRANSIENT_FOR); + prop_client(c, XA_WM_NORMAL_HINTS); + prop_client(c, XA_WM_HINTS); + prop_client(c, XA_WM_NAME); + + XSetWindowBorderWidth(blz.dpy, c->win, 0); + XAddToSaveSet(blz.dpy, c->win); + + fwa.override_redirect = True; + fwa.background_pixmap = ParentRelative; + fwa.backing_store = Always; + fwa.event_mask = + SubstructureRedirectMask + | SubstructureNotifyMask + | ExposureMask + | EnterWindowMask + | PointerMotionMask + | KeyPressMask + | ButtonPressMask + | ButtonReleaseMask; + c->framewin = XCreateWindow( + /* display */ blz.dpy, + /* parent */ blz.root, + /* x */ c->rect.x, + /* y */ c->rect.y, + /* width */ c->rect.width + 2 * def.border, + /* height */ c->rect.height + def.border + labelh(&def.font), + /* border */ 0, + /* depth */ DefaultDepth(blz.dpy, blz.screen), + /* class */ CopyFromParent, + /* visual */ DefaultVisual(blz.dpy, blz.screen), + /* valuemask */ CWOverrideRedirect | CWEventMask | CWBackPixmap | CWBackingStore, + /* attributes */&fwa + ); + + c->gc = XCreateGC(blz.dpy, c->framewin, 0, 0); + XSync(blz.dpy, False); + + for(t=&client ;; t=&(*t)->next) + if(!*t) { + c->next = *t; + *t = c; + break; + } + + write_event("CreateClient 0x%x\n", c->win); + return c; +} + +static int +dummy_error_handler(Display *dpy, XErrorEvent *error) { + return 0; +} + +void +destroy_client(Client *c) { + char *dummy; + Client **tc; + XEvent ev; + + XGrabServer(blz.dpy); + /* In case the client is already unmapped */ + XSetErrorHandler(dummy_error_handler); + + for(tc=&client; *tc; tc=&(*tc)->next) + if(*tc == c) { + *tc = c->next; + break; + } + + dummy = nil; + update_client_views(c, &dummy); + + unmap_client(c, WithdrawnState); + gravitate_client(c, True); + reparent_client(c, blz.root, c->rect.x, c->rect.y); + + XFreeGC(blz.dpy, c->gc); + XDestroyWindow(blz.dpy, c->framewin); + XSync(blz.dpy, False); + + XSetErrorHandler(wmii_error_handler); + XUngrabServer(blz.dpy); + flushevents(EnterWindowMask, False); + + while(XCheckMaskEvent(blz.dpy, StructureNotifyMask, &ev)) + if(ev.type != UnmapNotify || ev.xunmap.window != c->win) + dispatch_event(&ev); + + write_event("DestroyClient 0x%x\n", c->win); + free(c); +} + +void +manage_client(Client *c) { + XTextProperty tags = { 0 }; + Client *trans; + + XGetTextProperty(blz.dpy, c->win, &tags, atom[TagsAtom]); + + if((trans = client_of_win(c->trans))) + strncpy(c->tags, trans->tags, sizeof(c->tags)); + else if(tags.nitems) + strncpy(c->tags, (char *)tags.value, sizeof(c->tags)); + XFree(tags.value); + + gravitate_client(c, False); + reparent_client(c, c->framewin, def.border, labelh(&def.font)); + + if(!strlen(c->tags)) + apply_rules(c); + else + apply_tags(c, c->tags); + + if(!starting) + update_views(); + XSync(blz.dpy, False); + + if(c->sel->view == screen->sel) + focus(c, True); + flushevents(EnterWindowMask, False); +} + +Client * +sel_client() { + if(screen->sel && screen->sel->sel->sel) + return screen->sel->sel->sel->client; + return nil; +} + +Client * +client_of_win(Window w) { + Client *c; + for(c=client; c; c=c->next) + if(c->win == w) break; + return c; +} + +Frame * +frame_of_win(Window w) { + Client *c; + for(c=client; c; c=c->next) + if(c->framewin == w) break; + if(c) + return c->sel; + return nil; +} + +static void +update_client_name(Client *c) { + XTextProperty name; + XClassHint ch; + int n; + char **list = nil; + + name.nitems = 0; + c->name[0] = 0; + XGetTextProperty(blz.dpy, c->win, &name, atom[NetWMName]); + if(!name.nitems) + XGetWMName(blz.dpy, c->win, &name); + if(!name.nitems) + return; + if(name.encoding == XA_STRING) + strncpy(c->name, (char *)name.value, sizeof(c->name)); + else { + if(XmbTextPropertyToTextList(blz.dpy, &name, &list, &n) >= Success + && n > 0 && *list) + { + strncpy(c->name, *list, sizeof(c->name)); + XFreeStringList(list); + } + } + XFree(name.value); + if(XGetClassHint(blz.dpy, c->win, &ch)) { + snprintf(c->props, sizeof(c->props), + "%s:%s:%s", + str_nil(ch.res_class), + str_nil(ch.res_name), + c->name); + if(ch.res_class) + XFree(ch.res_class); + if(ch.res_name) + XFree(ch.res_name); + } +} + +void +update_client_grab(Client *c) { + Frame *f; + f = c->sel; + if((f->client != sel_client()) + || (f->area->floating && f != f->area->stack)) { + if(verbose) + fprintf(stderr, "update_client_grab(%p) AnyButton => %s\n", c, str_nil(c->name)); + grab_button(c->framewin, AnyButton, AnyModifier); + }else { + if(verbose) + fprintf(stderr, "update_client_grab(%p) def.mod => %s\n", c, str_nil(c->name)); + XUngrabButton(blz.dpy, AnyButton, AnyModifier, c->framewin); + grab_button(c->framewin, Button1, def.mod); + grab_button(c->framewin, Button3, def.mod); + } +} + +void +set_client_state(Client * c, int state) { + long data[] = { state, None }; + XChangeProperty( + /* display */ blz.dpy, + /* parent */ c->win, + /* property */ atom[WMState], + /* type */ atom[WMState], + /* format */ 32, + /* mode */ PropModeReplace, + /* data */ (uchar *) data, + /* npositions */2 + ); +} + +void +map_client(Client *c) { + if(!c->mapped) { + XSelectInput(blz.dpy, c->win, ClientMask & ~StructureNotifyMask); + XMapWindow(blz.dpy, c->win); + XSelectInput(blz.dpy, c->win, ClientMask); + set_client_state(c, NormalState); + c->mapped = 1; + } +} + +void +unmap_client(Client *c, int state) { + if(c->mapped) { + c->unmapped++; + XSelectInput(blz.dpy, c->win, ClientMask & ~StructureNotifyMask); + XUnmapWindow(blz.dpy, c->win); + XSelectInput(blz.dpy, c->win, ClientMask); + set_client_state(c, state); + c->mapped = 0; + } +} + +void +map_frame(Client *c) { + if(!c->frame_mapped) { + XMapWindow(blz.dpy, c->framewin); + c->frame_mapped = True; + } +} + +void +unmap_frame(Client *c) { + if(c->frame_mapped) { + XUnmapWindow(blz.dpy, c->framewin); + c->frame_mapped = False; + } +} + +void +reparent_client(Client *c, Window w, int x, int y) { + XSelectInput(blz.dpy, c->win, ClientMask & ~StructureNotifyMask); + XReparentWindow(blz.dpy, c->win, w, x, y); + XSelectInput(blz.dpy, c->win, ClientMask); +} + +void +set_cursor(Client *c, Cursor cur) { + XSetWindowAttributes wa; + + if(c->cursor != cur) { + c->cursor = cur; + wa.cursor = cur; + XChangeWindowAttributes(blz.dpy, c->framewin, CWCursor, &wa); + } +} + +void +configure_client(Client *c) { + XConfigureEvent e; + Frame *f; + + f = c->sel; + if(!f) + return; + + e.type = ConfigureNotify; + e.event = c->win; + e.window = c->win; + e.x = f->crect.x + f->rect.x - c->border; + e.y = f->crect.y + f->rect.y - c->border; + e.width = f->crect.width; + e.height = f->crect.height; + e.border_width = c->border; + e.above = None; + e.override_redirect = False; + XSendEvent(blz.dpy, c->win, False, + StructureNotifyMask, (XEvent *) & e); + XSync(blz.dpy, False); +} + +static void +send_client_message(Window w, Atom a, long value) { + XEvent e; + + e.type = ClientMessage; + e.xclient.window = w; + e.xclient.message_type = a; + e.xclient.format = 32; + e.xclient.data.l[0] = value; + e.xclient.data.l[1] = CurrentTime; + XSendEvent(blz.dpy, w, False, NoEventMask, &e); + XSync(blz.dpy, False); +} + +void +kill_client(Client * c) { + if(c->proto & WM_PROTOCOL_DELWIN) + send_client_message(c->win, atom[WMProtocols], atom[WMDelete]); + else + XKillClient(blz.dpy, c->win); +} + +static void +set_urgent(Client *c, Bool urgent, Bool write) { + XWMHints *wmh; + char *cwrite, *cnot; + Frame *f, *ff; + Area *a; + + cwrite = "Client"; + if(write) + cwrite = "Manager"; + cnot = "Not"; + if(urgent) + cnot = ""; + + if(urgent != c->urgent) { + write_event("%sUrgent 0x%x %s\n", cnot, c->win, cwrite); + c->urgent = urgent; + if(c->sel) { + if(c->sel->view == screen->sel) + draw_frame(c->sel); + if(!urgent || c->sel->view != screen->sel) + for(f=c->frame; f; f=f->cnext) { + for(a=f->view->area; a; a=a->next) + for(ff=a->frame; ff; ff=ff->anext) + if(ff->client->urgent) break; + if(!ff) + write_event("%sUrgentTag %s %s\n", cnot, cwrite, f->view->name); + } + } + } + + if(write) { + wmh = XGetWMHints(blz.dpy, c->win); + if(wmh) { + if(urgent) + wmh->flags |= XUrgencyHint; + else + wmh->flags &= ~XUrgencyHint; + XSetWMHints(blz.dpy, c->win, wmh); + XFree(wmh); + } + } +} + +void +prop_client(Client *c, Atom a) { + XWMHints *wmh; + long msize; + + if(a == atom[WMProtocols]) + c->proto = win_proto(c->win); + else if(a== atom[NetWMName]) { +wmname: + update_client_name(c); + if(c->frame) + draw_frame(c->sel); + }else switch (a) { + case XA_WM_TRANSIENT_FOR: + XGetTransientForHint(blz.dpy, c->win, &c->trans); + break; + case XA_WM_NORMAL_HINTS: + if(!XGetWMNormalHints(blz.dpy, c->win, &c->size, &msize) || !c->size.flags) + c->size.flags = PSize; + c->fixedsize = False; + if((c->size.flags & PMinSize) && (c->size.flags & PMaxSize) + &&(c->size.min_width == c->size.max_width) + &&(c->size.min_height == c->size.max_height)) + c->fixedsize = True; + break; + case XA_WM_HINTS: + wmh = XGetWMHints(blz.dpy, c->win); + if(wmh) { + set_urgent(c, (wmh->flags & XUrgencyHint) != 0, False); + XFree(wmh); + } + break; + case XA_WM_NAME: + goto wmname; + } +} + +void +gravitate_client(Client *c, Bool invert) { + int dx, dy; + int gravity; + + gravity = NorthWestGravity; + if(c->size.flags & PWinGravity) { + gravity = c->size.win_gravity; + } + + dy = 0; + switch (gravity) { + case StaticGravity: + case NorthWestGravity: + case NorthGravity: + case NorthEastGravity: + dy = labelh(&def.font); + break; + case EastGravity: + case CenterGravity: + case WestGravity: + dy = -(c->rect.height / 2) + labelh(&def.font); + break; + case SouthEastGravity: + case SouthGravity: + case SouthWestGravity: + dy = -c->rect.height; + break; + default: + break; + } + + dx = 0; + switch (gravity) { + case StaticGravity: + case NorthWestGravity: + case WestGravity: + case SouthWestGravity: + dx = def.border; + break; + case NorthGravity: + case CenterGravity: + case SouthGravity: + dx = -(c->rect.width / 2) + def.border; + break; + case NorthEastGravity: + case EastGravity: + case SouthEastGravity: + dx = -(c->rect.width + def.border); + break; + default: + break; + } + + if(invert) { + dx = -dx; + dy = -dy; + } + c->rect.x += dx; + c->rect.y += dy; +} + +void +apply_sizehints(Client *c, XRectangle *r, Bool floating, Bool frame, BlitzAlign sticky) { + XSizeHints *s; + XRectangle orig; + uint bw, bh; + + s = &c->size; + orig = *r; + if(frame) + frame2client(r); + bw = 0; + bh = 0; + + if(s->flags & PMinSize) { + bw = s->min_width; + bh = s->min_height; + if(floating) { + if(r->width < s->min_width) + r->width = s->min_width; + if(r->height < s->min_height) + r->height = s->min_height; + } + } + if(s->flags & PMaxSize) { + if(r->width > s->max_width) + r->width = s->max_width; + if(r->height > s->max_height) + r->height = s->max_height; + } + + if(s->flags & PBaseSize) { + bw = s->base_width; + bh = s->base_height; + } + + if(s->flags & PResizeInc) { + if(s->width_inc > 0) + r->width -= (r->width - bw) % s->width_inc; + if(s->height_inc > 0) + r->height -= (r->height - bh) % s->height_inc; + } + + if((s->flags & (PBaseSize|PMinSize)) == PMinSize) { + bw = 0; + bh = 0; + } + + if(s->flags & PAspect) { + double min, max, initial; + + min = (double)s->min_aspect.x / s->min_aspect.y; + max = (double)s->max_aspect.x / s->max_aspect.y; + initial = (double)(r->width - bw) / (r->height - bh); + if(initial < min) + r->height = bh + (r->width - bw) / min; + if(initial > max) + r->width = bw + (r->height - bh) * max; + } + + if(frame) + client2frame(r); + + if(!(s->flags & PMinSize) || !floating) { + if(r->width > orig.width) + r->width = orig.width; + if(r->height > orig.height) + r->height = orig.height; + } + + if((sticky & (EAST|WEST)) == EAST) + r->x = r_east(&orig) - r->width; + if((sticky & (NORTH|SOUTH)) == SOUTH) + r->y = r_south(&orig) - r->height; +} + +void +focus(Client *c, Bool restack) { + View *v; + Frame *f; + + f = c->sel; + if(!f) + return; + + v = f->area->view; + if(v != screen->sel) + focus_view(screen, v); + focus_frame(c->sel, restack); +} + +void +focus_client(Client *c) { + flushevents(FocusChangeMask, True); + + if(verbose) + fprintf(stderr, "focus_client(%p) => %s\n", c, (c ? c->name : nil)); + if(screen->focus != c) { + if(c && verbose) + fprintf(stderr, "\t%s => %s\n", (screen->focus ? screen->focus->name : "<nil>"), + (c ? c->name : "<nil>")); + if(c) { + XSetInputFocus(blz.dpy, c->win, RevertToParent, CurrentTime); + update_client_grab(c); + }else + XSetInputFocus(blz.dpy, screen->barwin, RevertToParent, CurrentTime); + } + + flushevents(FocusChangeMask, True); +} + +void +resize_client(Client *c, XRectangle *r) { + Frame *f; + + f = c->sel; + resize_frame(f, r); + + if(f->area->view != screen->sel) { + unmap_client(c, IconicState); + unmap_frame(c); + return; + } + + c->rect = f->crect; + c->rect.x += f->rect.x; + c->rect.y += f->rect.y; + if((f->area->mode == Colmax) + && (f->area->sel != f)) { + unmap_frame(c); + unmap_client(c, IconicState); + }else if(f->collapsed) { + XMoveResizeWindow(blz.dpy, c->framewin, + f->rect.x, f->rect.y, + f->rect.width, f->rect.height); + map_frame(c); + unmap_client(c, IconicState); + }else { + XMoveResizeWindow(blz.dpy, c->win, + f->crect.x, f->crect.y, + f->crect.width, f->crect.height); + map_client(c); + XMoveResizeWindow(blz.dpy, c->framewin, + f->rect.x, f->rect.y, + f->rect.width, f->rect.height); + map_frame(c); + configure_client(c); + } + + flushevents(FocusChangeMask|ExposureMask, True); +} + +void +newcol_client(Client *c, char *arg) { + Frame *f = c->sel; + Area *to, *a = f->area; + View *v = a->view; + + if(a->floating) + return; + if(!f->anext && f == a->frame) + return; + if(!strncmp(arg, "prev", 5)) { + for(to=v->area; to; to=to->next) + if(to->next == a) break; + to = new_column(v, to, 0); + send_to_area(to, f); + } + else if(!strncmp(arg, "next", 5)) { + to = new_column(v, a, 0); + send_to_area(to, f); + } + else + return; + flushevents(EnterWindowMask, False); +} + +void +move_client(Client *c, char *arg) { + Frame *f = c->sel; + XRectangle new = f->rect; + int x, y; + + if(sscanf(arg, "%d %d", &x, &y) != 2) + return; + new.x += x; + new.y += y; + if(!f->area->floating) + resize_column(f->client, &new); + else + resize_client(f->client, &new); +} + +void +size_client(Client *c, char *arg) { + Frame *f = c->sel; + XRectangle new = f->rect; + int w, h; + + if(sscanf(arg, "%d %d", &w, &h) != 2) + return; + new.width += w; + new.height += h; + if(!f->area->floating) + resize_column(f->client, &new); + else + resize_client(f->client, &new); +} + +char * +send_client(Frame *f, char *arg, Bool swap) { + Area *to, *a; + Client *c; + Frame *tf; + View *v; + Bool before; + int j; + + a = f->area; + v = a->view; + c = f->client; + if(!strncmp(arg, "toggle", 7)) { + if(!a->floating) + to = v->area; + else if(c->revert && !c->revert->floating) + to = c->revert; + else + to = v->area->next; + goto send_area; + }else if(!a->floating) { + if(!strncmp(arg, "left", 5)) { + if(a->floating) + return Ebadvalue; + for(to=v->area->next; to; to=to->next) + if(a == to->next) break; + if(!to && !swap && (f->anext || f != a->frame)) + to=new_column(v, v->area, 0); + goto send_area; + } + else if(!strncmp(arg, "right", 5)) { + if(a->floating) + return Ebadvalue; + to = a->next; + if(!to && !swap && (f->anext || f != a->frame)) + to = new_column(v, a, 0); + goto send_area; + } + else if(!strncmp(arg, "up", 3)) { + for(tf=a->frame; tf; tf=tf->anext) + if(tf->anext == f) break; + before = True; + goto send_frame; + } + else if(!strncmp(arg, "down", 5)) { + tf = f->anext; + before = False; + goto send_frame; + } + else { + if(sscanf(arg, "%d", &j) != 1) + return Ebadvalue; + for(to=v->area; to; to=to->next) + if(!--j) break; + goto send_area; + } + } + return Ebadvalue; + +send_frame: + if(!tf) + return Ebadvalue; + if(!swap) { + remove_frame(f); + insert_frame(tf, f, before); + }else + swap_frames(f, tf); + arrange_column(a, False); + + flushevents(EnterWindowMask, False); + focus_frame(f, True); + update_views(); + return nil; + +send_area: + if(!to) + return Ebadvalue; + if(!swap) + send_to_area(to, f); + else if(to->sel) + swap_frames(f, to->sel); + + flushevents(EnterWindowMask, False); + focus_frame(f, True); + update_views(); + return nil; +} + +void +update_client_views(Client *c, char **tags) { + Frame **fp, *f; + int cmp; + + fp = &c->frame; + while(*fp || *tags) { + while(*fp) { + if(*tags) { + cmp = strcmp((*fp)->view->name, *tags); + if(cmp >= 0) + break; + } + + f = *fp; + detach_from_area(f); + *fp = f->cnext; + if(c->sel == f) + c->sel = *fp; + free(f); + } + if(*tags) { + if(!*fp || cmp > 0) { + f = create_frame(c, get_view(*tags)); + if(f->view == screen->sel || !c->sel) + c->sel = f; + attach_to_view(f->view, f); + f->cnext = *fp; + *fp = f; + } + if(*fp) fp=&(*fp)->cnext; + tags++; + } + } + update_views(); +} + +static int +compare_tags(const void *a, const void *b) { + return strcmp(*(char **)a, *(char **)b); +} + +void +apply_tags(Client *c, const char *tags) { + uint i, j, k, n; + Bool add; + char buf[512], last; + char *toks[32], *cur; + + buf[0] = 0; + for(n = 0; tags[n]; n++) + if(tags[n] != ' ' && tags[n] != '\t') break; + if(tags[n] == '+' || tags[n] == '-') + strncpy(buf, c->tags, sizeof(c->tags)); + strlcat(buf, &tags[n], sizeof(buf)); + trim(buf, " \t/"); + + n = 0; + j = 0; + add = True; + if(buf[0] == '+') + n++; + else if(buf[0] == '-') { + n++; + add = False; + } + while(buf[n] && n < sizeof(buf) && j < 32) { + for(i = n; i < sizeof(buf) - 1; i++) + if(buf[i] == '+' + || buf[i] == '-' + || buf[i] == '\0') + break; + last = buf[i]; + buf[i] = '\0'; + + cur = nil; + if(!strncmp(&buf[n], "~", 2)) + c->floating = add; + else if(!strncmp(&buf[n], "!", 2)) + cur = view ? screen->sel->name : "nil"; + else if(strncmp(&buf[n], "sel", 4) + && strncmp(&buf[n], ".", 2) + && strncmp(&buf[n], "..", 3)) + cur = &buf[n]; + + n = i + 1; + if(cur) { + if(add) + toks[j++] = cur; + else { + for(i = 0, k = 0; i < j; i++) + if(strcmp(toks[i], cur)) + toks[k++] = toks[i]; + j = k; + } + } + + switch(last) { + case '+': + add = True; + break; + case '-': + add = False; + break; + case '\0': + buf[n] = '\0'; + break; + } + } + + c->tags[0] = '\0'; + if(!j) + return; + qsort(toks, j, sizeof(char *), compare_tags); + + for(i=0, n=0; i < j; i++) + if(!n || strcmp(toks[i], toks[n-1])) { + if(i) + strlcat(c->tags, "+", sizeof(c->tags)); + strlcat(c->tags, toks[i], sizeof(c->tags)); + toks[n++] = toks[i]; + } + toks[n] = nil; + + update_client_views(c, toks); + XChangeProperty(blz.dpy, c->win, atom[TagsAtom], XA_STRING, 8, + PropModeReplace, (uchar *)c->tags, strlen(c->tags)); +} + +void +apply_rules(Client *c) { + Rule *r; + regmatch_t rm; + + if(strlen(c->tags)) + return; + if(def.tagrules.string) + for(r=def.tagrules.rule; r; r=r->next) + if(!regexec(&r->regex, c->props, 1, &rm, 0)) { + apply_tags(c, r->value); + if(strlen(c->tags) && strcmp(c->tags, "nil")) + break; + } + if(!strlen(c->tags)) + apply_tags(c, "nil"); +} + +char * +message_client(Client *c, char *message) { + if(!strncmp(message, "kill", 5)) + kill_client(c); + else if(!strncmp(message, "Urgent", 7)) + set_urgent(c, True, True); + else if(!strncmp(message, "NotUrgent", 10)) + set_urgent(c, False, True); + else + return Ebadcmd; + return nil; +} diff --git a/cmd/wmii/column.c b/cmd/wmii/column.c @@ -0,0 +1,317 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <math.h> +#include <string.h> +#include "wmii.h" + +char * +str_of_column_mode(int mode) { + switch(mode) { + case Coldefault: return "default"; break; + case Colstack: return "stack"; break; + case Colmax: return "max"; break; + default: break; + } + return nil; +} + +int +column_mode_of_str(char *arg) { + if(!strncmp("default", arg, 8)) + return Coldefault; + if(!strncmp("stack", arg, 6)) + return Colstack; + if(!strncmp("max", arg, 4)) + return Colmax; + return -1; +} + +static void +scale_column(Area *a) { + Frame *f, **fp; + uint min_height, yoff, dy; + uint num_col, num_uncol; + uint col_h, uncol_h; + int surplus, i, j; + + if(!a->frame) + return; + + /* This works by comparing heights based on a surplus of their + * minimum size. We start by subtracting the minimum size, then + * scale the surplus, and add back the minimum size later. This + * is based on the size of the client, rather than the frame, so + * increment gaps can be equalized later */ + /* Frames that can't be accomodated are pushed to the floating layer */ + + min_height = labelh(&def.font); + col_h = labelh(&def.font); + uncol_h = min_height + frame_delta_h(); + + num_col = 0; + num_uncol = 0; + dy = 0; + for(f=a->frame; f; f=f->anext) + if(f->collapsed) + num_col++; + else + num_uncol++; + + surplus = a->rect.height; + surplus -= num_col * col_h; + surplus -= num_uncol * uncol_h; + if(surplus < 0) { + i = ceil((float)(-surplus)/(uncol_h - col_h)); + if(i >= num_uncol) + i = num_uncol - 1; + num_uncol -= i; + num_col += i; + surplus += i * (uncol_h - col_h); + } + if(surplus < 0) { + i = ceil((float)(-surplus)/col_h); + if(i > num_col) + i = num_col; + num_col -= i; + surplus += i * col_h; + } + + i = num_col - 1; + j = num_uncol - 1; + for(f=a->frame; f; f=f->anext) { + if(f == a->sel) + j++; + if(!f->collapsed) { + if(j < 0 && f != a->sel) + f->collapsed = True; + else { + if(f->crect.height <= min_height) + f->crect.height = 1; + else + f->crect.height -= min_height; + dy += f->crect.height; + } + j--; + } + } + for(fp=&a->frame; *fp;) { + f = *fp; + if(f == a->sel) + i++; + if(f->collapsed) { + if(i < 0 && f != a->sel) { + f->collapsed = False; + send_to_area(f->view->area, f); + continue; + } + i--; + } + fp=&f->anext; + } + + i = num_uncol; + for(f=a->frame; f; f=f->anext) { + f->rect.x = a->rect.x; + f->rect.width = a->rect.width; + if(!f->collapsed) { + i--; + f->rect.height = (float)f->crect.height / dy * surplus; + if(!i) + f->rect.height = surplus; + f->rect.height += min_height + frame_delta_h(); + apply_sizehints(f->client, &f->rect, False, True, NWEST); + + dy -= f->crect.height; + surplus -= f->rect.height - frame_delta_h() - min_height; + }else + f->rect.height = labelh(&def.font); + } + + yoff = a->rect.y; + i = num_uncol; + for(f=a->frame; f; f=f->anext) { + f->rect.y = yoff; + f->rect.x = a->rect.x; + f->rect.width = a->rect.width; + if(f->collapsed) + yoff += f->rect.height; + else{ + i--; + f->rect.height += surplus / num_uncol; + if(!i) + f->rect.height += surplus % num_uncol; + yoff += f->rect.height; + } + } +} + +void +arrange_column(Area *a, Bool dirty) { + Frame *f; + + if(a->floating || !a->frame) + return; + + switch(a->mode) { + case Coldefault: + for(f=a->frame; f; f=f->anext) { + f->collapsed = False; + if(dirty) + f->crect.height = 100; + } + break; + case Colstack: + for(f=a->frame; f; f=f->anext) + f->collapsed = (f != a->sel); + break; + case Colmax: + for(f=a->frame; f; f=f->anext) { + f->collapsed = False; + f->rect = a->rect; + } + goto resize; + default: + break; + } + scale_column(a); +resize: + if(a->view == screen->sel) { + restack_view(a->view); + resize_client(a->sel->client, &a->sel->rect); + for(f=a->frame; f; f=f->anext) + if(!f->collapsed && f != a->sel) + resize_client(f->client, &f->rect); + for(f=a->frame; f; f=f->anext) + if(f->collapsed && f != a->sel) + resize_client(f->client, &f->rect); + } +} + +static void +match_horiz(Area *a, XRectangle *r) { + Frame *f; + + for(f=a->frame; f; f=f->anext) { + f->rect.x = r->x; + f->rect.width = r->width; + resize_frame(f, &f->rect); + } +} + +void +resize_column(Client *c, XRectangle *new) { + Area *west, *east, *a; + Frame *north, *south, *f; + View *v; + BlitzAlign sticky; + uint min_height; + uint min_width; + + f = c->sel; + a = f->area; + v = a->view; + min_height = 2 * labelh(&def.font); + min_width = screen->rect.width/NCOL; + + for(west=v->area->next; west; west=west->next) + if(west->next == a) break; + east = a->next; + for(north=a->frame; north; north=north->anext) + if(north->anext == f) break; + south = f->anext; + /* validate (and trim if necessary) horizontal resize */ + sticky = get_sticky(&f->rect, new); + if(new->width < min_width) { + if(sticky & EAST) + new->x = r_east(&a->rect) - min_width; + new->width = min_width; + } + if(west && !(sticky & WEST)) { + if(new->x < 0 || new->x < (west->rect.x + min_width)) { + new->width -= (west->rect.x + min_width) - new->x; + new->x = west->rect.x + min_width; + } + } else { + new->width += new->x - a->rect.x; + new->x = a->rect.x; + } + if(east && !(sticky & EAST)) { + if(r_east(new) > r_east(&east->rect) - min_width) + new->width = r_east(&east->rect) - min_width - new->x; + } else + new->width = r_east(&a->rect) - new->x; + if(new->width < min_width) + goto AfterHorizontal; + /* horizontal resize */ + sticky = get_sticky(&a->rect, new); + if(west && !(sticky & WEST)) { + west->rect.width = new->x - west->rect.x; + a->rect.width += a->rect.x - new->x; + a->rect.x = new->x; + match_horiz(a, &a->rect); + match_horiz(west, &west->rect); + //relax_column(west); + } + if(east && !(sticky & EAST)) { + east->rect.width -= r_east(new) - east->rect.x; + east->rect.x = r_east(new); + a->rect.width = r_east(new) - a->rect.x; + match_horiz(a, &a->rect); + match_horiz(east, &east->rect); + //relax_column(east); + } +AfterHorizontal: + /* skip vertical resize unless the column is in equal mode */ + if(a->mode != Coldefault) + goto AfterVertical; + /* validate (and trim if necessary) vertical resize */ + sticky = get_sticky(&f->rect, new); + if(new->height < min_height) { + if((f->rect.height < min_height) && sticky & (NORTH|SOUTH)) + goto AfterVertical; + if(sticky & SOUTH) + new->y = r_south(&f->rect) - min_height; + new->height = min_height; + } + if(north && !(sticky & NORTH)) + if(new->y < 0 || new->y < (north->rect.y + min_height)) { + new->height -= (north->rect.y + min_height) - new->y; + new->y = north->rect.y + min_height; + } + if(south && !(sticky & SOUTH)) { + if(r_south(new) > r_south(&south->rect) - min_height) + new->height = r_south(&south->rect) - min_height - new->y; + } + if(new->height < min_height) + goto AfterVertical; + /* vertical resize */ + if(north && !(sticky & NORTH)) { + north->rect.height = new->y - north->rect.y; + f->rect.height += f->rect.y - new->y; + f->rect.y = new->y; + resize_frame(north, &north->rect); + resize_frame(f, &f->rect); + } + if(south && !(sticky & SOUTH)) { + south->rect.height -= r_south(new) - south->rect.y; + south->rect.y = r_south(new); + f->rect.y = new->y; + f->rect.height = new->height; + resize_frame(f, &f->rect); + resize_frame(south, &south->rect); + } +AfterVertical: + arrange_column(a, False); + focus_view(screen, v); +} + +Area * +new_column(View *v, Area *pos, uint w) { + Area *a = create_area(v, pos, w); + if(!a) + return nil; + arrange_view(v); + return a; +} diff --git a/cmd/wmii/draw.c b/cmd/wmii/draw.c @@ -0,0 +1,227 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ +#include <stdlib.h> +#include <string.h> +#include "wmii.h" + +uint +textwidth_l(BlitzFont *font, char *text, uint len) { + if(font->set) { + XRectangle r; + XmbTextExtents(font->set, text, len, &r, nil); + return r.width; + } + return XTextWidth(font->xfont, text, len); +} + +uint +textwidth(BlitzFont *font, char *text) { + return textwidth_l(font, text, strlen(text)); +} + +void +loadfont(Blitz *blitz, BlitzFont *font) { + char *fontname = font->fontstr; + char **missing = nil, *def = "?"; + int n; + + if(font->set) + XFreeFontSet(blitz->dpy, font->set); + font->set = XCreateFontSet(blitz->dpy, fontname, &missing, &n, &def); + if(missing) { + while(n--) + fprintf(stderr, "wmii: missing fontset for '%s': %s\n", + fontname, missing[n]); + XFreeStringList(missing); + } + if(font->set) { + XFontSetExtents *font_extents; + XFontStruct **xfonts; + char **font_names; + + font->ascent = font->descent = 0; + font_extents = XExtentsOfFontSet(font->set); + XFontsOfFontSet(font->set, &xfonts, &font_names); + font->ascent = xfonts[0]->ascent; + font->descent = xfonts[0]->descent; + } + else { + if(font->xfont) + XFreeFont(blitz->dpy, font->xfont); + font->xfont = nil; + font->xfont = XLoadQueryFont(blitz->dpy, fontname); + if (!font->xfont) { + if(!strncmp(fontname, BLITZ_FONT, sizeof(BLITZ_FONT))) + fatal("cannot load font: %s", + BLITZ_FONT); + free(font->fontstr); + font->fontstr = estrdup(BLITZ_FONT); + loadfont(blitz, font); + return; + } + font->ascent = font->xfont->ascent; + font->descent = font->xfont->descent; + } + font->height = font->ascent + font->descent; +} + +uint +labelh(BlitzFont *font) { + return font->height + 2; +} + +void +draw_tile(BlitzBrush *b) { + drawbg(b->blitz->dpy, b->drawable, b->gc, &b->rect, + b->color, True, b->border); +} + +void +draw_border(BlitzBrush *b) { + drawbg(b->blitz->dpy, b->drawable, b->gc, &b->rect, + b->color, False, b->border); +} + +void +draw_label(BlitzBrush *b, char *text) { + uint x, y, w, h, len; + Bool shortened = False; + static char buf[2048]; + XRectangle r = {0}; + XGCValues gcv; + + draw_tile(b); + if(!text) + return; + shortened = 0; + strncpy(buf, text, sizeof(buf)); + len = strlen(buf); + gcv.foreground = b->color.fg; + gcv.background = b->color.bg; + h = b->font->ascent + b->font->descent; + y = b->rect.y + b->rect.height / 2 - h / 2 + b->font->ascent; + /* shorten text if necessary */ + while(len + && (w = textwidth(b->font, buf)) > b->rect.width - (b->font->height & ~1)) { + buf[--len] = 0; + shortened = True; + } + if(!len) + return; + if(w > b->rect.width) + return; + /* mark shortened info in the string */ + if(shortened) { + if (len > 3) + buf[len - 3] = '.'; + if (len > 2) + buf[len - 2] = '.'; + if (len > 1) + buf[len - 1] = '.'; + } + + if(b->font->set) { + XmbTextExtents(b->font->set, text, len, &r, nil); + } + + switch (b->align) { + case EAST: + x = b->rect.x + b->rect.width - (w + (b->font->height / 2)); + break; + default: + x = b->rect.x + (b->font->height / 2) - r.x; + break; + } + if(b->font->set) { + XChangeGC(b->blitz->dpy, b->gc, GCForeground | GCBackground, &gcv); + XmbDrawImageString(b->blitz->dpy, b->drawable, b->font->set, b->gc, + x, y, buf, len); + } + else { + gcv.font = b->font->xfont->fid; + XChangeGC(b->blitz->dpy, b->gc, GCForeground | GCBackground | GCFont, &gcv); + XDrawImageString(b->blitz->dpy, b->drawable, b->gc, x, y, buf, len); + } +} + +void +drawbg(Display *dpy, Drawable drawable, GC gc, XRectangle *rect, + BlitzColor c, Bool fill, int border) +{ + if(fill) { + XSetForeground(dpy, gc, c.bg); + XFillRectangles(dpy, drawable, gc, rect, 1); + } + if(border) { + XSetLineAttributes(dpy, gc, border, LineSolid, CapButt, JoinMiter); + XSetForeground(dpy, gc, c.border); + XDrawRectangle(dpy, drawable, gc, rect->x + border / 2, rect->y + border / 2, + rect->width - border, rect->height - border); + } +} + +void +drawcursor(Display *dpy, Drawable drawable, GC gc, + int x, int y, uint h, BlitzColor c) +{ + XSegment s[5]; + + XSetForeground(dpy, gc, c.fg); + XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); + s[0].x1 = x - 1; + s[0].y1 = s[0].y2 = y; + s[0].x2 = x + 2; + s[1].x1 = x - 1; + s[1].y1 = s[1].y2 = y + 1; + s[1].x2 = x + 2; + s[2].x1 = s[2].x2 = x; + s[2].y1 = y; + s[2].y2 = y + h; + s[3].x1 = x - 1; + s[3].y1 = s[3].y2 = y + h; + s[3].x2 = x + 2; + s[4].x1 = x - 1; + s[4].y1 = s[4].y2 = y + h - 1; + s[4].x2 = x + 2; + XDrawSegments(dpy, drawable, gc, s, 5); +} + +static ulong +xloadcolor(Blitz *blitz, char *colstr) { + XColor color; + char col[8]; + + strncpy(col, colstr, sizeof(col)); + col[7] = 0; + XAllocNamedColor(blitz->dpy, + DefaultColormap(blitz->dpy, blitz->screen), col, &color, &color); + return color.pixel; +} + +int +loadcolor(Blitz *blitz, BlitzColor *c) { + if(!c->colstr || strlen(c->colstr) != 23) + return -1; + c->fg = xloadcolor(blitz, &c->colstr[0]); + c->bg = xloadcolor(blitz, &c->colstr[8]); + c->border = xloadcolor(blitz, &c->colstr[16]); + return 0; +} + +char * +parse_colors(char **buf, int *buflen, BlitzColor *col) { + uint i; + if(*buflen < 23 || 3 != sscanf(*buf, "#%06x #%06x #%06x", &i,&i,&i)) + return "bad value"; + (*buflen) -= 23; + bcopy(*buf, col->colstr, 23); + loadcolor(&blz, col); + + (*buf) += 23; + if(**buf == '\n' || **buf == ' ') { + (*buf)++; + (*buflen)--; + } + return nil; +} diff --git a/cmd/wmii/event.c b/cmd/wmii/event.c @@ -0,0 +1,448 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <X11/keysym.h> +#include "wmii.h" +#include "printevent.h" + +void +dispatch_event(XEvent *e) { + if(handler[e->type]) + handler[e->type](e); +} + +uint +flushevents(long event_mask, Bool dispatch) { + XEvent ev; + uint n = 0; + + while(XCheckMaskEvent(blz.dpy, event_mask, &ev)) { + if(dispatch) + dispatch_event(&ev); + n++; + } + return n; +} + +static void +buttonrelease(XEvent *e) { + XButtonPressedEvent *ev; + Frame *f; + Bar *b; + + ev = &e->xbutton; + if(ev->window == screen->barwin) { + for(b=screen->bar[BarLeft]; b; b=b->next) + if(ptinrect(ev->x, ev->y, &b->brush.rect)) { + write_event("LeftBarClick %d %s\n", ev->button, b->name); + return; + } + for(b=screen->bar[BarRight]; b; b=b->next) + if(ptinrect(ev->x, ev->y, &b->brush.rect)) { + write_event("RightBarClick %d %s\n", ev->button, b->name); + return; + } + } + else if((f = frame_of_win(ev->window))) + write_event("ClientClick 0x%x %d\n", f->client->win, ev->button); +} + +static void +buttonpress(XEvent *e) { + XButtonPressedEvent *ev; + Frame *f; + + ev = &e->xbutton; + if((f = frame_of_win(ev->window))) { + if((ev->state & def.mod) == def.mod) { + switch(ev->button) { + case Button1: + do_mouse_resize(f->client, False, CENTER); + focus(f->client, True); + frame_to_top(f); + focus(f->client, True); + break; + case Button3: + do_mouse_resize(f->client, False, + quadrant(&f->rect, ev->x_root, ev->y_root)); + frame_to_top(f); + focus(f->client, True); + break; + default: break; + XAllowEvents(blz.dpy, ReplayPointer, ev->time); + } + }else{ + if(ev->button == Button1) { + if(frame_to_top(f)) + restack_view(f->view); + + if(ptinrect(ev->x, ev->y, &f->grabbox)) + do_mouse_resize(f->client, True, CENTER); + else if(!ev->subwindow && !ptinrect(ev->x, ev->y, &f->titlebar)) + do_mouse_resize(f->client, False, quadrant(&f->rect, ev->x_root, ev->y_root)); + + if(f->client != sel_client()) + focus(f->client, True); + } + if(ev->subwindow) + XAllowEvents(blz.dpy, ReplayPointer, ev->time); + else { + /* Ungrab so a menu can receive events before the button is released */ + XUngrabPointer(blz.dpy, ev->time); + XSync(blz.dpy, False); + + write_event("ClientMouseDown 0x%x %d\n", f->client->win, ev->button); + } + } + }else + XAllowEvents(blz.dpy, ReplayPointer, ev->time); +} + +static void +configurerequest(XEvent *e) { + XConfigureRequestEvent *ev; + XWindowChanges wc; + XRectangle *frect; + Client *c; + Frame *f; + + ev = &e->xconfigurerequest; + c = client_of_win(ev->window); + if(c) { + f = c->sel; + gravitate_client(c, True); + if(ev->value_mask & CWX) + c->rect.x = ev->x; + if(ev->value_mask & CWY) + c->rect.y = ev->y; + if(ev->value_mask & CWWidth) + c->rect.width = ev->width; + if(ev->value_mask & CWHeight) + c->rect.height = ev->height; + if(ev->value_mask & CWBorderWidth) + c->border = ev->border_width; + gravitate_client(c, False); + + if((c->rect.height == screen->rect.height) + && (c->rect.width == screen->rect.width)) { + c->fullscreen = True; + if(c->sel) { + if(!c->sel->area->floating) + send_to_area(c->sel->view->area, c->sel); + focus_client(c); + restack_view(c->sel->view); + } + } + + if(c->sel->area->floating) + frect=&c->sel->rect; + else + frect=&c->sel->revert; + + *frect = c->rect; + frect->y -= labelh(&def.font); + frect->x -= def.border; + frect->width += 2 * def.border; + frect->height += frame_delta_h(); + + if(c->sel->area->floating || c->fullscreen) + resize_client(c, frect); + else + configure_client(c); + }else{ + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + ev->value_mask &= ~(CWStackMode|CWSibling); + XConfigureWindow(blz.dpy, ev->window, ev->value_mask, &wc); + XSync(blz.dpy, False); + } +} + +static void +destroynotify(XEvent *e) { + XDestroyWindowEvent *ev; + Client *c; + + ev = &e->xdestroywindow; + if((c = client_of_win(ev->window))) + destroy_client(c); +} + +static void +enternotify(XEvent *e) { + XCrossingEvent *ev; + Client *c; + Frame *f; + + ev = &e->xcrossing; + if(ev->mode != NotifyNormal) + return; + + if((c = client_of_win(ev->window))) { + if(ev->detail != NotifyInferior) { + if(screen->focus != c) { + if(verbose) fprintf(stderr, "enter_notify(c) => %s\n", c->name); + focus(c, False); + } + set_cursor(c, cursor[CurNormal]); + }else if(verbose) fprintf(stderr, "enter_notify(c[NotifyInferior]) => %s\n", c->name); + } + else if((f = frame_of_win(ev->window))) { + if(screen->focus != c) { + if(verbose) fprintf(stderr, "enter_notify(f) => %s\n", f->client->name); + if(f->area->floating || !f->collapsed) + focus(f->client, False); + } + set_frame_cursor(f, ev->x, ev->y); + } + else if(ev->window == blz.root) { + sel_screen = True; + draw_frames(); + } +} + +static void +leavenotify(XEvent *e) { + XCrossingEvent *ev; + + ev = &e->xcrossing; + if((ev->window == blz.root) && !ev->same_screen) { + sel_screen = True; + draw_frames(); + } +} + +void +print_focus(Client *c, char *to) { + if(verbose) { + fprintf(stderr, "screen->focus: %p => %p\n", + screen->focus, c); + fprintf(stderr, "\t%s => %s\n", + screen->focus ? screen->focus->name : "<nil>", + to); + } +} + +static void +focusin(XEvent *e) { + XFocusChangeEvent *ev; + Client *c, *old; + XEvent me; + + ev = &e->xfocus; + /* Yes, we're focusing in on nothing, here. */ + if(ev->detail == NotifyDetailNone) { + XSetInputFocus(blz.dpy, screen->barwin, RevertToParent, CurrentTime); + return; + } + + if(!((ev->detail == NotifyNonlinear) + ||(ev->detail == NotifyNonlinearVirtual) + ||(ev->detail == NotifyVirtual) + ||(ev->detail == NotifyInferior) + ||(ev->detail == NotifyAncestor))) + return; + if((ev->mode == NotifyWhileGrabbed) + && (screen->hasgrab != &c_root)) + return; + + old = screen->focus; + c = client_of_win(ev->window); + if(c) { + print_focus(c, c->name); + if(ev->mode == NotifyGrab) + screen->hasgrab = c; + screen->focus = c; + if(c != old) { + update_client_grab(c); + if(c->sel) + draw_frame(c->sel); + if(old && old->sel) + draw_frame(old->sel); + } + }else if(ev->window == screen->barwin) { + print_focus(nil, "<nil>"); + screen->focus = nil; + }else if(ev->mode == NotifyGrab) { + if(ev->window == blz.root) + if(XCheckMaskEvent(blz.dpy, KeyPressMask, &me)) { + /* wmii has grabbed focus */ + screen->hasgrab = &c_root; + dispatch_event(&me); + return; + } + /* Some unmanaged window has grabbed focus */ + if((c = screen->focus)) { + print_focus(&c_magic, "<magic>"); + screen->focus = &c_magic; + if(c->sel) + draw_frame(c->sel); + } + } +} + +static void +focusout(XEvent *e) { + XFocusChangeEvent *ev; + Client *c; + + ev = &e->xfocus; + if(!((ev->detail == NotifyNonlinear) + ||(ev->detail == NotifyNonlinearVirtual))) + return; + if(ev->mode == NotifyUngrab) + screen->hasgrab = nil; + + c = client_of_win(ev->window); + if(c) { + if((ev->mode == NotifyWhileGrabbed) + && (screen->hasgrab != &c_root)) { + if((screen->focus) + && (screen->hasgrab != screen->focus)) + screen->hasgrab = screen->focus; + if(screen->hasgrab == c) + return; + }else if(ev->mode != NotifyGrab) { + if(screen->focus == c) { + print_focus(&c_magic, "<magic>"); + screen->focus = &c_magic; + } + update_client_grab(c); + if(c->sel) + draw_frame(c->sel); + } + } +} + +static void +expose(XEvent *e) { + XExposeEvent *ev; + static Frame *f; + + ev = &e->xexpose; + if(ev->count == 0) { + if(ev->window == screen->barwin) + draw_bar(screen); + else if((f = frame_of_win(ev->window))) + draw_frame(f); + } +} + +static void +keypress(XEvent *e) { + XKeyEvent *ev; + + ev = &e->xkey; + ev->state &= valid_mask; + if(ev->window == blz.root) + kpress(blz.root, ev->state, (KeyCode) ev->keycode); +} + +static void +mappingnotify(XEvent *e) { + XMappingEvent *ev; + + ev = &e->xmapping; + XRefreshKeyboardMapping(ev); + if(ev->request == MappingKeyboard) + update_keys(); +} + +static void +maprequest(XEvent *e) { + XMapRequestEvent *ev; + static XWindowAttributes wa; + + ev = &e->xmaprequest; + if(!XGetWindowAttributes(blz.dpy, ev->window, &wa)) + return; + if(wa.override_redirect) { + XSelectInput(blz.dpy, ev->window, + (StructureNotifyMask | PropertyChangeMask)); + return; + } + if(!client_of_win(ev->window)) + manage_client(create_client(ev->window, &wa)); +} + +static void +motionnotify(XEvent *e) { + XMotionEvent *ev; + Frame *f; + + ev = &e->xmotion; + if((f = frame_of_win(ev->window))) + set_frame_cursor(f, ev->x, ev->y); +} + +static void +propertynotify(XEvent *e) { + XPropertyEvent *ev; + Client *c; + + ev = &e->xproperty; + if(ev->state == PropertyDelete) + return; /* ignore */ + if((c = client_of_win(ev->window))) + prop_client(c, ev->atom); +} + +static void +mapnotify(XEvent *e) { + XMapEvent *ev; + Client *c; + + ev = &e->xmap; + if((c = client_of_win(ev->window))) + if(c == sel_client()) + focus_client(c); +} + +static void +unmapnotify(XEvent *e) { + XUnmapEvent *ev; + Client *c; + + ev = &e->xunmap; + if((c = client_of_win(ev->window))) + if(ev->send_event || (c->unmapped-- == 0)) + destroy_client(c); +} + +void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ButtonRelease] = buttonrelease, + [ConfigureRequest]=configurerequest, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [FocusOut] = focusout, + [KeyPress] = keypress, + [LeaveNotify] = leavenotify, + [MapNotify] = mapnotify, + [MapRequest] = maprequest, + [MappingNotify] = mappingnotify, + [MotionNotify] = motionnotify, + [PropertyNotify]= propertynotify, + [UnmapNotify] = unmapnotify, + +}; + +void +check_x_event(IxpConn *c) { + XEvent ev; + while(XPending(blz.dpy)) { + XNextEvent(blz.dpy, &ev); + if(verbose) + printevent(&ev); + dispatch_event(&ev); + XPending(blz.dpy); + } +} diff --git a/cmd/wmii/frame.c b/cmd/wmii/frame.c @@ -0,0 +1,341 @@ +/* Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include "wmii.h" + +Frame * +create_frame(Client *c, View *v) { + static ushort id = 1; + Frame *f = emallocz(sizeof(Frame)); + + f->id = id++; + f->client = c; + f->view = v; + if(c->sel) { + f->revert = c->sel->revert; + f->rect = c->sel->rect; + } + else{ + c->sel = f; + f->revert = f->rect = c->rect; + f->revert.width = f->rect.width += 2 * def.border; + f->revert.height = f->rect.height += frame_delta_h(); + } + f->collapsed = False; + + return f; +} + +void +remove_frame(Frame *f) { + Area *a; + Frame **ft; + + a = f->area; + for(ft = &a->frame; *ft; ft=&(*ft)->anext) + if(*ft == f) break; + *ft = f->anext; + + if(a->floating) { + for(ft = &a->stack; *ft; ft=&(*ft)->snext) + if(*ft == f) break; + *ft = f->snext; + } +} + +void +insert_frame(Frame *pos, Frame *f, Bool before) { + Frame *ft, **p; + Area *a = f->area; + + if(before) { + for(ft=a->frame; ft; ft=ft->anext) + if(ft->anext == pos) break; + pos=ft; + } + p = &a->frame; + if(pos) + p = &pos->anext; + f->anext = *p; + *p = f; + + if(a->floating) { + f->snext = a->stack; + a->stack = f; + } +} + +void +frame2client(XRectangle *r) { + r->width = max(r->width - def.border * 2, 1); + r->height = max(r->height - frame_delta_h(), 1); +} + +void +client2frame(XRectangle *r) { + r->width += def.border * 2; + r->height += frame_delta_h(); +} + +void +resize_frame(Frame *f, XRectangle *r) { + BlitzAlign stickycorner; + Client *c; + + c = f->client; + stickycorner = get_sticky(&f->rect, r); + + f->rect = *r; + f->crect = *r; + apply_sizehints(c, &f->crect, f->area->floating, True, stickycorner); + + if(f->area->floating) + f->rect = f->crect; + + frame2client(&f->crect); + + if(f->crect.height < labelh(&def.font)) + f->collapsed = True; + else + f->collapsed = False; + + if(f->crect.width < labelh(&def.font)) { + f->rect.width = frame_delta_h(); + f->collapsed = True; + } + + if(f->collapsed) { + f->rect.height = labelh(&def.font); + f->crect = f->rect; + } + f->crect.y = labelh(&def.font); + f->crect.x = (f->rect.width - f->crect.width) / 2; + + + if(f->area->floating) { + if(c->fullscreen) { + f->crect.width = screen->rect.width; + f->crect.height = screen->rect.height; + f->rect = f->crect; + client2frame(&f->rect); + f->rect.x = -def.border; + f->rect.y = -labelh(&def.font); + }else + check_frame_constraints(&f->rect); + } +} + +void +set_frame_cursor(Frame *f, int x, int y) { + XRectangle r; + Cursor cur; + + if(!ptinrect(x, y, &f->titlebar) + &&!ptinrect(x, y, &f->crect)) { + r = f->rect; + r.x = 0; + r.y = 0; + cur = cursor_of_quad(quadrant(&r, x, y)); + set_cursor(f->client, cur); + }else + set_cursor(f->client, cursor[CurNormal]); +} + +Bool +frame_to_top(Frame *f) { + Frame **tf; + Area *a; + + a = f->area; + if(!a->floating || f == a->stack) + return False; + for(tf=&a->stack; *tf; tf=&(*tf)->snext) + if(*tf == f) break; + *tf = f->snext; + f->snext = a->stack; + a->stack = f; + update_client_grab(f->client); + return True; +} + +void +swap_frames(Frame *fa, Frame *fb) { + XRectangle trect; + Area *a; + Frame **fp_a, **fp_b, *ft; + + if(fa == fb) return; + + a = fa->area; + for(fp_a = &a->frame; *fp_a; fp_a = &(*fp_a)->anext) + if(*fp_a == fa) break; + a = fb->area; + for(fp_b = &a->frame; *fp_b; fp_b = &(*fp_b)->anext) + if(*fp_b == fb) break; + + if(fa->anext == fb) { + *fp_a = fb; + fa->anext = fb->anext; + fb->anext = fa; + } else if(fb->anext == fa) { + *fp_b = fa; + fb->anext = fa->anext; + fa->anext = fb; + } else { + *fp_a = fb; + *fp_b = fa; + ft = fb->anext; + fb->anext = fa->anext; + fa->anext = ft; + } + + if(fb->area->sel == fb) + fb->area->sel = fa; + if(fa->area->sel == fa) + fa->area->sel = fb; + + fb->area = fa->area; + fa->area = a; + + trect = fa->rect; + fa->rect = fb->rect; + fb->rect = trect; +} + +void +focus_frame(Frame *f, Bool restack) { + Frame *old, *old_in_a; + View *v; + Area *a, *old_a; + + a = f->area; + v = f->view; + old = v->sel->sel; + old_a = v->sel; + old_in_a = a->sel; + + a->sel = f; + + if(a != old_a) + focus_area(f->area); + + if(v != screen->sel) + return; + + focus_client(f->client); + + if(!a->floating + && ((a->mode == Colstack) || (a->mode == Colmax))) + arrange_column(a, False); + + if((f != old) + && (f->area == old_a)) + write_event("ClientFocus 0x%x\n", f->client->win); + + if(restack) + restack_view(v); +} + +int +frame_delta_h() { + return def.border + labelh(&def.font); +} + +void +draw_frame(Frame *f) { + BlitzBrush br = { 0 }; + Frame *tf; + + if(f->view != screen->sel) + return; + + br.blitz = &blz; + br.font = &def.font; + br.drawable = pmap; + br.gc = f->client->gc; + if(f->client == screen->focus) + br.color = def.focuscolor; + else + br.color = def.normcolor; + if(!f->area->floating && f->area->mode == Colmax) + for(tf = f->area->frame; tf; tf=tf->anext) + if(tf->client == screen->focus) { + br.color = def.focuscolor; + break; + } + + br.rect = f->rect; + br.rect.x = 0; + br.rect.y = 0; + draw_tile(&br); + + br.rect.x += def.font.height - 3; + br.rect.width -= br.rect.x; + br.rect.height = labelh(&def.font); + draw_label(&br, f->client->name); + + br.border = 1; + br.rect.width += br.rect.x; + br.rect.x = 0; + f->titlebar.x = br.rect.x + 3; + f->titlebar.height = br.rect.height - 3; + f->titlebar.y = br.rect.y + 3; + f->titlebar.width = br.rect.width - 6; + draw_border(&br); + br.rect.height = f->rect.height; + if(def.border) + draw_border(&br); + + if(f->client->urgent) + br.color.bg = br.color.fg; + br.rect.x = 2; + br.rect.y = 2; + br.rect.height = labelh(&def.font) - 4; + br.rect.width = def.font.height - 3; + f->grabbox = br.rect; + draw_tile(&br); + + XCopyArea( + /* display */ blz.dpy, + /* src */ pmap, + /* dest */ f->client->framewin, + /* gc */ f->client->gc, + /* x, y */ 0, 0, + /* width */ f->rect.width, + /* height */ f->rect.height, + /* dest_x */ 0, + /* dest_y */ 0 + ); + XSync(blz.dpy, False); +} + +void +draw_frames() { + Client *c; + + for(c=client; c; c=c->next) + if(c->sel && c->sel->view == screen->sel) + draw_frame(c->sel); +} + +void +check_frame_constraints(XRectangle *rect) { + int max_height; + int barheight; + + barheight = screen->brect.height; + max_height = screen->rect.height - barheight; + + if(rect->height > max_height) + rect->height = max_height; + if(rect->width > screen->rect.width) + rect->width = screen->rect.width; + if(rect->x + barheight > screen->rect.width) + rect->x = screen->rect.width - barheight; + if(rect->y + barheight > max_height) + rect->y = max_height - barheight; + if(r_east(rect) < barheight) + rect->x = barheight - rect->width; + if(r_south(rect) < barheight) + rect->y = barheight - rect->height; +} diff --git a/cmd/wmii/fs.c b/cmd/wmii/fs.c @@ -0,0 +1,974 @@ +/* Copyright ©2006 Kris Maglione <fbsdaemon at gmail dot com> + * See LICENSE file for license details. + */ +#include <assert.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "wmii.h" + + +/* Datatypes: */ +/**************/ +typedef struct Dirtab Dirtab; +struct Dirtab { + char *name; + uchar qtype; + uint type; + uint perm; +}; + +typedef struct FidLink FidLink; +struct FidLink { + FidLink *next; + Fid *fid; +}; + +typedef struct FileId FileId; +struct FileId { + FileId *next; + union { + void *ref; + char *buf; + Bar *bar; + Bar **bar_p; + View *view; + Client *client; + Ruleset *rule; + BlitzColor *col; + } content; + uint id; + uint index; + Dirtab tab; + ushort nref; +}; + +/* Constants */ +/*************/ +enum { /* Dirs */ + FsRoot, FsDClient, FsDClients, FsDBars, + FsDTag, FsDTags, + /* Files */ + FsFBar, FsFCctl, FsFColRules, + FsFCtags, FsFEvent, FsFKeys, FsFRctl, + FsFTagRules, FsFTctl, FsFTindex, + FsFprops +}; + +/* Error messages */ +static char + Enoperm[] = "permission denied", + Enofile[] = "file not found", + Ebadvalue[] = "bad value", + Einterrupted[] = "interrupted", + Ebadcmd[] = "bad command"; + +/* Macros */ +#define QID(t, i) (((vlong)((t)&0xFF)<<32)|((i)&0xFFFFFFFF)) + +/* Global Vars */ +/***************/ +FileId *free_fileid; +Ixp9Req *pending_event_reads; +Ixp9Req *outgoing_event_reads; +FidLink *pending_event_fids; +Ixp9Srv p9srv = { + .open= fs_open, + .walk= fs_walk, + .read= fs_read, + .stat= fs_stat, + .write= fs_write, + .clunk= fs_clunk, + .flush= fs_flush, + .attach=fs_attach, + .create=fs_create, + .remove=fs_remove, + .freefid=fs_freefid +}; + +/* ad-hoc file tree. Empty names ("") indicate dynamic entries to be filled + * in by lookup_file */ +static Dirtab +dirtab_root[]= {{".", QTDIR, FsRoot, 0500|P9_DMDIR }, + {"rbar", QTDIR, FsDBars, 0700|P9_DMDIR }, + {"lbar", QTDIR, FsDBars, 0700|P9_DMDIR }, + {"client", QTDIR, FsDClients, 0500|P9_DMDIR }, + {"tag", QTDIR, FsDTags, 0500|P9_DMDIR }, + {"ctl", QTAPPEND, FsFRctl, 0600|P9_DMAPPEND }, + {"colrules", QTFILE, FsFColRules, 0600 }, + {"event", QTFILE, FsFEvent, 0600 }, + {"keys", QTFILE, FsFKeys, 0600 }, + {"tagrules", QTFILE, FsFTagRules, 0600 }, + {nil}}, +dirtab_clients[]={{".", QTDIR, FsDClients, 0500|P9_DMDIR }, + {"", QTDIR, FsDClient, 0500|P9_DMDIR }, + {nil}}, +dirtab_client[]= {{".", QTDIR, FsDClient, 0500|P9_DMDIR }, + {"ctl", QTAPPEND, FsFCctl, 0600|P9_DMAPPEND }, + {"tags", QTFILE, FsFCtags, 0600 }, + {"props", QTFILE, FsFprops, 0400 }, + {nil}}, +dirtab_bars[]= {{".", QTDIR, FsDBars, 0700|P9_DMDIR }, + {"", QTFILE, FsFBar, 0600 }, + {nil}}, +dirtab_tags[]= {{".", QTDIR, FsDTags, 0500|P9_DMDIR }, + {"", QTDIR, FsDTag, 0500|P9_DMDIR }, + {nil}}, +dirtab_tag[]= {{".", QTDIR, FsDTag, 0500|P9_DMDIR }, + {"ctl", QTAPPEND, FsFTctl, 0600|P9_DMAPPEND }, + {"index", QTFILE, FsFTindex, 0400 }, + {nil}}; +/* Writing the lists separately and using an array of their references + * removes the need for casting and allows for C90 conformance, + * since otherwise we would need to use compound literals */ +static Dirtab *dirtab[] = { + [FsRoot] = dirtab_root, + [FsDBars] = dirtab_bars, + [FsDClients] = dirtab_clients, + [FsDClient] = dirtab_client, + [FsDTags] = dirtab_tags, + [FsDTag] = dirtab_tag, +}; + +/* Utility Functions */ +/*********************/ + +/* get_file/free_file save and reuse old FileId structs + * since so many of them are needed for so many + * purposes */ +static FileId * +get_file() { + FileId *temp; + if(!free_fileid) { + uint i = 15; + temp = emallocz(sizeof(FileId) * i); + for(; i; i--) { + temp->next = free_fileid; + free_fileid = temp++; + } + } + temp = free_fileid; + free_fileid = temp->next; + temp->nref = 1; + temp->next = nil; + return temp; +} + +static void +free_file(FileId *f) { + if(--f->nref) + return; + free(f->tab.name); + f->next = free_fileid; + free_fileid = f; +} + +/* This function's name belies it's true purpose. It increases + * the reference counts of the FileId list */ +static void +clone_files(FileId *f) { + for(; f; f=f->next) + assert(f->nref++); +} + +/* This should be moved to libixp */ +static void +write_buf(Ixp9Req *r, char *buf, uint len) { + if(r->ifcall.offset >= len) + return; + + len -= r->ifcall.offset; + if(len > r->ifcall.count) + len = r->ifcall.count; + r->ofcall.data = emalloc(len); + memcpy(r->ofcall.data, buf + r->ifcall.offset, len); + r->ofcall.count = len; +} + +/* This should be moved to libixp */ +void +write_to_buf(Ixp9Req *r, void *buf, uint *len, uint max) { + uint offset, count; + + offset = (r->fid->omode&P9_OAPPEND) ? *len : r->ifcall.offset; + if(offset > *len || r->ifcall.count == 0) { + r->ofcall.count = 0; + return; + } + + count = r->ifcall.count; + if(max && (count > max - offset)) + count = max - offset; + + *len = offset + count; + + if(max == 0) { + *(void **)buf = erealloc(*(void **)buf, *len + 1); + buf = *(void **)buf; + } + + memcpy((uchar*)buf + offset, r->ifcall.data, count); + r->ofcall.count = count; + ((char *)buf)[offset+count] = '\0'; +} + +/* This should be moved to libixp */ +void +data_to_cstring(Ixp9Req *r) { + uint i; + i = r->ifcall.count; + if(!i || r->ifcall.data[i - 1] != '\n') + r->ifcall.data = erealloc(r->ifcall.data, ++i); + assert(r->ifcall.data); + r->ifcall.data[i - 1] = '\0'; +} + +char * +message_root(char *message) +{ + uint n; + + if(!strchr(message, ' ')) { + snprintf(buffer, BUFFER_SIZE, "%s ", message); + message = buffer; + } + if(!strcmp(message, "quit ")) + srv.running = 0; + else if(!strncmp(message, "exec ", 5)) { + srv.running = 0; + execstr = estrdup(&message[5]); + message += strlen(message); + } + else if(!strncmp(message, "view ", 5)) + select_view(&message[5]); + else if(!strncmp(message, "selcolors ", 10)) { + fprintf(stderr, "wmii: warning: selcolors have been removed\n"); + return Ebadcmd; + } + else if(!strncmp(message, "focuscolors ", 12)) { + message += 12; + n = strlen(message); + return parse_colors(&message, (int *)&n, &def.focuscolor); + } + else if(!strncmp(message, "normcolors ", 11)) { + message += 11; + n = strlen(message); + return parse_colors(&message, (int *)&n, &def.normcolor); + } + else if(!strncmp(message, "font ", 5)) { + message += 5; + free(def.font.fontstr); + def.font.fontstr = estrdup(message); + loadfont(&blz, &def.font); + resize_bar(screen); + } + else if(!strncmp(message, "border ", 7)) { + message += 7; + n = (uint)strtol(message, &message, 10); + if(*message) + return Ebadvalue; + def.border = n; + } + else if(!strncmp(message, "grabmod ", 8)) { + message += 8; + ulong mod; + mod = mod_key_of_str(message); + if(!(mod & (Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))) + return Ebadvalue; + strncpy(def.grabmod, message, sizeof(def.grabmod)); + def.mod = mod; + if(view) + restack_view(screen->sel); + } + else + return Ebadcmd; + return nil; +} + +char * +read_root_ctl() { + uint i = 0; + if(screen->sel) + i += snprintf(&buffer[i], (BUFFER_SIZE - i), "view %s\n", screen->sel->name); + i += snprintf(&buffer[i], (BUFFER_SIZE - i), "focuscolors %s\n", def.focuscolor.colstr); + i += snprintf(&buffer[i], (BUFFER_SIZE - i), "normcolors %s\n", def.normcolor.colstr); + i += snprintf(&buffer[i], (BUFFER_SIZE - i), "font %s\n", def.font.fontstr); + i += snprintf(&buffer[i], (BUFFER_SIZE - i), "grabmod %s\n", def.grabmod); + i += snprintf(&buffer[i], (BUFFER_SIZE - i), "border %d\n", def.border); + return buffer; +} + + +void +respond_event(Ixp9Req *r) { + FileId *f = r->fid->aux; + if(f->content.buf) { + r->ofcall.data = (void *)f->content.buf; + r->ofcall.count = strlen(f->content.buf); + respond(r, nil); + f->content.buf = nil; + }else{ + r->aux = pending_event_reads; + pending_event_reads = r; + } +} + +void +write_event(char *format, ...) { + uint len, slen; + va_list ap; + FidLink *f; + FileId *fi; + Ixp9Req *req; + + va_start(ap, format); + vsnprintf(buffer, BUFFER_SIZE, format, ap); + va_end(ap); + if(!(len = strlen(buffer))) + return; + for(f=pending_event_fids; f; f=f->next) { + fi = f->fid->aux; + slen = fi->content.buf ? strlen(fi->content.buf) : 0; + fi->content.buf = (char *) erealloc(fi->content.buf, slen + len + 1); + (fi->content.buf)[slen] = '\0'; + strcat(fi->content.buf, buffer); + } + outgoing_event_reads = pending_event_reads; + pending_event_reads = nil; + while((req = outgoing_event_reads)) { + outgoing_event_reads = outgoing_event_reads->aux; + respond_event(req); + } +} + +static void +dostat(Stat *s, uint len, FileId *f) { + s->type = 0; + s->dev = 0; + s->qid.path = QID(f->tab.type, f->id); + s->qid.version = 0; + s->qid.type = f->tab.qtype; + s->mode = f->tab.perm; + s->atime = time(nil); + s->mtime = time(nil); + s->length = len; + s->name = f->tab.name; + s->uid = user; + s->gid = user; + s->muid = user; +} + +/* lookup_file */ +/***************/ +/* All lookups and directory organization should be performed through + * lookup_file, mostly through the dirtabs[] tree. */ +static FileId * +lookup_file(FileId *parent, char *name) +{ + FileId *ret, *file, **last; + Dirtab *dir; + Client *c; + View *v; + Bar *b; + uint id; + + if(!(parent->tab.perm & P9_DMDIR)) + return nil; + dir = dirtab[parent->tab.type]; + last = &ret; + ret = nil; + for(; dir->name; dir++) { + /* Dynamic dirs */ + if(!*dir->name) { /* strlen(dir->name) == 0 */ + switch(parent->tab.type) { + case FsDClients: + if(!name || !strcmp(name, "sel")) { + if((c = sel_client())) { + file = get_file(); + *last = file; + last = &file->next; + file->content.client = c; + file->id = c->win; + file->index = c->win; + file->tab = *dir; + file->tab.name = estrdup("sel"); + }if(name) goto LastItem; + } + if(name) { + id = (uint)strtol(name, &name, 16); + if(*name) goto NextItem; + } + for(c=client; c; c=c->next) { + if(!name || c->win == id) { + file = get_file(); + *last = file; + last = &file->next; + file->content.client = c; + file->id = c->win; + file->index = c->win; + file->tab = *dir; + file->tab.name = emallocz(16); + snprintf(file->tab.name, 16, "0x%x", (uint)c->win); + if(name) goto LastItem; + } + } + break; + case FsDTags: + if(!name || !strcmp(name, "sel")) { + if(screen->sel) { + file = get_file(); + *last = file; + last = &file->next; + file->content.view = screen->sel; + file->id = screen->sel->id; + file->tab = *dir; + file->tab.name = estrdup("sel"); + }if(name) goto LastItem; + } + for(v=view; v; v=v->next) { + if(!name || !strcmp(name, v->name)) { + file = get_file(); + *last = file; + last = &file->next; + file->content.view = v; + file->id = v->id; + file->tab = *dir; + file->tab.name = estrdup(v->name); + if(name) goto LastItem; + } + } + break; + case FsDBars: + for(b=*parent->content.bar_p; b; b=b->next) { + if(!name || !strcmp(name, b->name)) { + file = get_file(); + *last = file; + last = &file->next; + file->content.bar = b; + file->id = b->id; + file->tab = *dir; + file->tab.name = estrdup(b->name); + if(name) goto LastItem; + } + } + break; + } + }else /* Static dirs */ + if(!name || !strcmp(name, dir->name)) { + file = get_file(); + *last = file; + last = &file->next; + file->id = 0; + file->content.ref = parent->content.ref; + file->index = parent->index; + file->tab = *dir; + file->tab.name = estrdup(file->tab.name); + /* Special considerations: */ + switch(file->tab.type) { + case FsDBars: + if(!strcmp(file->tab.name, "lbar")) + file->content.bar_p = &screen[0].bar[BarLeft]; + else + file->content.bar_p = &screen[0].bar[BarRight]; + break; + case FsFColRules: + file->content.rule = &def.colrules; + break; + case FsFTagRules: + file->content.rule = &def.tagrules; + break; + } + if(name) goto LastItem; + } + NextItem: + continue; + } +LastItem: + *last = nil; + return ret; +} + +Bool +verify_file(FileId *f) { + FileId *nf; + + if(!f->next) + return True; + if(verify_file(f->next)) { + nf = lookup_file(f->next, f->tab.name); + if(nf) { + free_file(nf); + return True; + } + } + return False; +} + +/* Service Functions */ +/*********************/ +void +fs_attach(Ixp9Req *r) { + FileId *f = get_file(); + f->tab = dirtab[FsRoot][0]; + f->tab.name = estrdup("/"); + f->content.ref = nil; /* shut up valgrind */ + r->fid->aux = f; + r->fid->qid.type = f->tab.qtype; + r->fid->qid.path = QID(f->tab.type, 0); + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +void +fs_walk(Ixp9Req *r) { + FileId *f, *nf; + int i; + + f = r->fid->aux; + clone_files(f); + for(i=0; i < r->ifcall.nwname; i++) { + if(!strcmp(r->ifcall.wname[i], "..")) { + if(f->next) { + nf=f; + f=f->next; + free_file(nf); + } + }else{ + nf = lookup_file(f, r->ifcall.wname[i]); + if(!nf) + break; + assert(!nf->next); + if(strcmp(r->ifcall.wname[i], ".")) { + nf->next = f; + f = nf; + } + } + r->ofcall.wqid[i].type = f->tab.qtype; + r->ofcall.wqid[i].path = QID(f->tab.type, f->id); + } + /* There should be a way to do this on freefid() */ + if(i < r->ifcall.nwname) { + while((nf = f)) { + f=f->next; + free_file(nf); + } + respond(r, Enofile); + return; + } + /* Remove refs for r->fid if no new fid */ + if(r->ifcall.fid == r->ifcall.newfid) { + nf = r->fid->aux; + r->fid->aux = f; + while((f = nf)) { + nf = nf->next; + free_file(f); + } + }else + r->newfid->aux = f; + r->ofcall.nwqid = i; + respond(r, nil); +} + +uint +fs_size(FileId *f) { + switch(f->tab.type) { + default: + return 0; + case FsFColRules: + case FsFTagRules: + return f->content.rule->size; + case FsFKeys: + return def.keyssz; + case FsFCtags: + return strlen(f->content.client->tags); + case FsFprops: + return strlen(f->content.client->props); + } +} + +void +fs_stat(Ixp9Req *r) { + Message m; + Stat s; + int size; + uchar *buf; + FileId *f = r->fid->aux; + + if(!verify_file(f)) { + respond(r, Enofile); + return; + } + + dostat(&s, fs_size(f), f); + r->ofcall.nstat = size = ixp_sizeof_stat(&s); + buf = emallocz(size); + + m = ixp_message(buf, size, MsgPack); + ixp_pstat(&m, &s); + + r->ofcall.stat = m.data; + respond(r, nil); +} + +void +fs_read(Ixp9Req *r) { + char *buf; + FileId *f, *tf; + int n, offset; + int size; + + offset = 0; + f = r->fid->aux; + + if(!verify_file(f)) { + respond(r, Enofile); + return; + } + + if(f->tab.perm & P9_DMDIR && f->tab.perm & 0400) { + Stat s; + Message m; + + offset = 0; + size = r->ifcall.count; + buf = emallocz(size); + m = ixp_message(buf, size, MsgPack); + + tf = f = lookup_file(f, nil); + /* Note: f->tab.name == "." so we skip it */ + for(f=f->next; f; f=f->next) { + dostat(&s, fs_size(f), f); + n = ixp_sizeof_stat(&s); + if(offset >= r->ifcall.offset) { + if(size < n) + break; + ixp_pstat(&m, &s); + } + offset += n; + } + while((f = tf)) { + tf=tf->next; + free_file(f); + } + r->ofcall.count = r->ifcall.count - size; + r->ofcall.data = m.data; + respond(r, nil); + return; + } + else{ + switch(f->tab.type) { + case FsFprops: + write_buf(r, f->content.client->props, strlen(f->content.client->props)); + respond(r, nil); + return; + case FsFColRules: + case FsFTagRules: + write_buf(r, f->content.rule->string, f->content.rule->size); + respond(r, nil); + return; + case FsFKeys: + write_buf(r, def.keys, def.keyssz); + respond(r, nil); + return; + case FsFCtags: + write_buf(r, f->content.client->tags, strlen(f->content.client->tags)); + respond(r, nil); + return; + case FsFTctl: + write_buf(r, f->content.view->name, strlen(f->content.view->name)); + respond(r, nil); + return; + case FsFBar: + write_buf(r, f->content.bar->buf, strlen(f->content.bar->buf)); + respond(r, nil); + return; + case FsFRctl: + buf = read_root_ctl(); + write_buf(r, buf, strlen(buf)); + respond(r, nil); + return; + case FsFCctl: + if(r->ifcall.offset) { + respond(r, nil); + return; + } + r->ofcall.data = emallocz(16); + n = snprintf(r->ofcall.data, 16, "0x%x", (uint)f->index); + assert(n >= 0); + r->ofcall.count = n; + respond(r, nil); + return; + case FsFTindex: + buf = (char *)view_index(f->content.view); + n = strlen(buf); + write_buf(r, buf, n); + respond(r, nil); + return; + case FsFEvent: + respond_event(r); + return; + } + } + /* This is an assert because it should this should not be called if + * the file is not open for reading. */ + assert(!"Read called on an unreadable file"); +} + +/* This function needs to be seriously cleaned up */ +void +fs_write(Ixp9Req *r) { + FileId *f; + char *errstr = nil; + uint i; + + if(r->ifcall.count == 0) { + respond(r, nil); + return; + } + f = r->fid->aux; + + if(!verify_file(f)) { + respond(r, Enofile); + return; + } + + switch(f->tab.type) { + case FsFColRules: + case FsFTagRules: + write_to_buf(r, &f->content.rule->string, &f->content.rule->size, 0); + respond(r, nil); + return; + case FsFKeys: + write_to_buf(r, &def.keys, &def.keyssz, 0); + respond(r, nil); + return; + case FsFCtags: + data_to_cstring(r); + apply_tags(f->content.client, r->ifcall.data); + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + case FsFBar: + /* XXX: This should validate after each write */ + i = strlen(f->content.bar->buf); + write_to_buf(r, &f->content.bar->buf, &i, 279); + r->ofcall.count = i - r->ifcall.offset; + respond(r, nil); + return; + case FsFCctl: + data_to_cstring(r); + if((errstr = message_client(f->content.client, r->ifcall.data))) { + respond(r, errstr); + return; + } + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + case FsFTctl: + data_to_cstring(r); + if((errstr = message_view(f->content.view, r->ifcall.data))) { + respond(r, errstr); + return; + } + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + case FsFRctl: + data_to_cstring(r); + { uint n; + char *toks[32]; + n = tokenize(toks, 32, r->ifcall.data, '\n'); + for(i = 0; i < n; i++) { + if(errstr) + message_root(toks[i]); + else + errstr = message_root(toks[i]); + } + } + if(screen->sel) + focus_view(screen, screen->sel); + if(errstr) { + respond(r, errstr); + return; + } + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + case FsFEvent: + if(r->ifcall.data[r->ifcall.count-1] == '\n') + write_event("%.*s", r->ifcall.count, r->ifcall.data); + else + write_event("%.*s\n", r->ifcall.count, r->ifcall.data); + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + } + /* This is an assert because this function should not be called if + * the file is not open for writing. */ + assert(!"Write called on an unwritable file"); +} + +void +fs_open(Ixp9Req *r) { + FidLink *fl; + FileId *f = r->fid->aux; + + if(!verify_file(f)) { + respond(r, Enofile); + return; + } + + switch(f->tab.type) { + case FsFEvent: + fl = emallocz(sizeof(FidLink)); + fl->fid = r->fid; + fl->next = pending_event_fids; + pending_event_fids = fl; + break; + } + if((r->ifcall.mode&3) == P9_OEXEC) { + respond(r, Enoperm); + return; + } + if((r->ifcall.mode&3) != P9_OREAD && !(f->tab.perm & 0200)) { + respond(r, Enoperm); + return; + } + if((r->ifcall.mode&3) != P9_OWRITE && !(f->tab.perm & 0400)) { + respond(r, Enoperm); + return; + } + if((r->ifcall.mode&~(3|P9_OAPPEND|P9_OTRUNC))) { + respond(r, Enoperm); + return; + } + respond(r, nil); +} + +void +fs_create(Ixp9Req *r) { + FileId *f = r->fid->aux; + + switch(f->tab.type) { + default: + /* XXX: This should be taken care of by the library */ + respond(r, Enoperm); + return; + case FsDBars: + if(!strlen(r->ifcall.name)) { + respond(r, Ebadvalue); + return; + } + create_bar(f->content.bar_p, r->ifcall.name); + f = lookup_file(f, r->ifcall.name); + if(!f) { + respond(r, Enofile); + return; + } + r->ofcall.qid.type = f->tab.qtype; + r->ofcall.qid.path = QID(f->tab.type, f->id); + f->next = r->fid->aux; + r->fid->aux = f; + respond(r, nil); + break; + } +} + +void +fs_remove(Ixp9Req *r) { + FileId *f = r->fid->aux; + + if(!verify_file(f)) { + respond(r, Enofile); + return; + } + + + switch(f->tab.type) { + default: + /* XXX: This should be taken care of by the library */ + respond(r, Enoperm); + return; + case FsFBar: + destroy_bar(f->next->content.bar_p, f->content.bar); + draw_bar(screen); + respond(r, nil); + break; + } +} + +void +fs_clunk(Ixp9Req *r) { + Client *c; + FidLink **fl, *ft; + char *buf; + int i; + FileId *f = r->fid->aux; + + if(!verify_file(f)) { + respond(r, nil); + return; + } + + switch(f->tab.type) { + case FsFColRules: + update_rules(&f->content.rule->rule, f->content.rule->string); + break; + case FsFTagRules: + update_rules(&f->content.rule->rule, f->content.rule->string); + for(c=client; c; c=c->next) + apply_rules(c); + update_views(); + break; + case FsFKeys: + update_keys(); + break; + case FsFBar: + buf = f->content.bar->buf; + i = strlen(f->content.bar->buf); + parse_colors(&buf, &i, &f->content.bar->brush.color); + while(i > 0 && buf[i - 1] == '\n') + buf[--i] = '\0'; + strncpy(f->content.bar->text, buf, sizeof(f->content.bar->text)); + draw_bar(screen); + break; + case FsFEvent: + for(fl=&pending_event_fids; *fl; fl=&(*fl)->next) + if((*fl)->fid == r->fid) { + ft = *fl; + *fl = (*fl)->next; + f = ft->fid->aux; + free(f->content.buf); + free(ft); + break; + } + break; + } + respond(r, nil); +} + +void +fs_flush(Ixp9Req *r) { + Ixp9Req **i, **j; + + for(i=&pending_event_reads; i != &outgoing_event_reads; i=&outgoing_event_reads) + for(j=i; *j; j=(Ixp9Req **)&(*j)->aux) + if(*j == r->oldreq) { + *j = (*j)->aux; + respond(r->oldreq, Einterrupted); + goto done; + } +done: + respond(r, nil); +} + +void +fs_freefid(Fid *f) { + FileId *id, *tid; + + for(id=f->aux; id; id = tid) { + tid = id->next; + free_file(id); + } +} diff --git a/cmd/wmii/geom.c b/cmd/wmii/geom.c @@ -0,0 +1,84 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ +#include "wmii.h" + +Bool +ptinrect(int x, int y, XRectangle * r) { + return (x >= r->x) && (x < r_east(r)) + && (y >= r->y) && (y < r_south(r)); +} + +BlitzAlign +quadrant(XRectangle *rect, int x, int y) { + BlitzAlign ret = 0; + x -= rect->x; + y -= rect->y; + + if(x >= rect->width * .5) + ret |= EAST; + if(x <= rect->width * .5) + ret |= WEST; + if(y <= rect->height * .5) + ret |= NORTH; + if(y >= rect->height * .5) + ret |= SOUTH; + + return ret; +} + +Cursor +cursor_of_quad(BlitzAlign align) { + switch(align) { + case NEAST: + return cursor[CurNECorner]; + case NWEST: + return cursor[CurNWCorner]; + case SEAST: + return cursor[CurSECorner]; + case SWEST: + return cursor[CurSWCorner]; + default: + return cursor[CurMove]; + } +} + +/* Syntax: <x> <y> <width> <height> */ +int +strtorect(XRectangle *r, const char *val) { + XRectangle new; + if (!val) + return -1; + + if(sscanf(val, "%hd %hd %hu %hu", &new.x, &new.y, &new.width, &new.height) != 4) + return -1; + + *r = new; + return 0; +} + +int +r_east(XRectangle *r) { + return r->x + r->width; +} + +int +r_south(XRectangle *r) { + return r->y + r->height; +} + +BlitzAlign +get_sticky(XRectangle *src, XRectangle *dst) { + BlitzAlign stickycorner = 0; + + if(src->x != dst->x && r_east(src) == r_east(dst)) + stickycorner |= EAST; + else + stickycorner |= WEST; + if(src->y != dst->y && r_south(src) == r_south(dst)) + stickycorner |= SOUTH; + else + stickycorner |= NORTH; + + return stickycorner; +} diff --git a/cmd/wmii/key.c b/cmd/wmii/key.c @@ -0,0 +1,243 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ +#include <string.h> +#include <stdlib.h> +#include <X11/keysym.h> +#include "wmii.h" + +void +init_lock_keys() { + XModifierKeymap *modmap; + KeyCode num_lock; + static int masks[] = { + ShiftMask, LockMask, ControlMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask + }; + int i; + + num_lock_mask = 0; + modmap = XGetModifierMapping(blz.dpy); + num_lock = XKeysymToKeycode(blz.dpy, XStringToKeysym("Num_Lock")); + if(modmap && modmap->max_keypermod > 0) { + int max = (sizeof(masks) / sizeof(int)) * modmap->max_keypermod; + for(i = 0; i < max; i++) + if(num_lock && (modmap->modifiermap[i] == num_lock)) + num_lock_mask = masks[i / modmap->max_keypermod]; + } + XFreeModifiermap(modmap); + valid_mask = 255 & ~(num_lock_mask | LockMask); +} + +ulong +mod_key_of_str(char *val) { + ulong mod = 0; + + if (strstr(val, "Shift")) + mod |= ShiftMask; + if (strstr(val, "Control")) + mod |= ControlMask; + if (strstr(val, "Mod1")) + mod |= Mod1Mask; + if (strstr(val, "Mod2")) + mod |= Mod2Mask; + if (strstr(val, "Mod3")) + mod |= Mod3Mask; + if (strstr(val, "Mod4")) + mod |= Mod4Mask; + if (strstr(val, "Mod5")) + mod |= Mod5Mask; + return mod; +} + +static void +grab_key(Key *k) { + XGrabKey(blz.dpy, k->key, k->mod, blz.root, + True, GrabModeAsync, GrabModeAsync); + if(num_lock_mask) { + XGrabKey(blz.dpy, k->key, k->mod | num_lock_mask, blz.root, + True, GrabModeAsync, GrabModeAsync); + XGrabKey(blz.dpy, k->key, k->mod | num_lock_mask | LockMask, blz.root, + True, GrabModeAsync, GrabModeAsync); + } + XSync(blz.dpy, False); +} + +static void +ungrab_key(Key *k) { + XUngrabKey(blz.dpy, k->key, k->mod, blz.root); + if(num_lock_mask) { + XUngrabKey(blz.dpy, k->key, k->mod | num_lock_mask, blz.root); + XUngrabKey(blz.dpy, k->key, k->mod | num_lock_mask | LockMask, blz.root); + } + XSync(blz.dpy, False); +} + +static Key * +name2key(const char *name) { + Key *k; + for(k=key; k; k=k->lnext) + if(!strncmp(k->name, name, sizeof(k->name))) break; + return k; +} + +static Key * +get_key(const char *name) { + char buf[128]; + char *seq[8]; + char *kstr; + uint i, toks; + static ushort id = 1; + Key *k = 0, *r = 0; + + if((k = name2key(name))) { + ungrab_key(k); + return k; + } + strncpy(buf, name, sizeof(buf)); + toks = tokenize(seq, 8, buf, ','); + for(i = 0; i < toks; i++) { + if(!k) + r = k = emallocz(sizeof(Key)); + else { + k->next = emallocz(sizeof(Key)); + k = k->next; + } + strncpy(k->name, name, sizeof(k->name)); + kstr = strrchr(seq[i], '-'); + if(kstr) + kstr++; + else + kstr = seq[i]; + k->key = XKeysymToKeycode(blz.dpy, XStringToKeysym(kstr)); + k->mod = mod_key_of_str(seq[i]); + } + if(r) { + r->id = id++; + r->lnext = key; + key = r; + } + + return r; +} + +static void +next_keystroke(ulong *mod, KeyCode *code) { + XEvent e; + KeySym sym; + *mod = 0; + + do { + XMaskEvent(blz.dpy, KeyPressMask, &e); + *mod |= e.xkey.state & valid_mask; + *code = (KeyCode) e.xkey.keycode; + sym = XKeycodeToKeysym(blz.dpy, e.xkey.keycode, 0); + } while(IsModifierKey(sym)); +} + +static void +emulate_key_press(ulong mod, KeyCode key) { + XEvent e; + Window client_win; + int revert; + + XGetInputFocus(blz.dpy, &client_win, &revert); + e.xkey.type = KeyPress; + e.xkey.time = CurrentTime; + e.xkey.window = client_win; + e.xkey.display = blz.dpy; + e.xkey.state = mod; + e.xkey.keycode = key; + XSendEvent(blz.dpy, client_win, True, KeyPressMask, &e); + e.xkey.type = KeyRelease; + XSendEvent(blz.dpy, client_win, True, KeyReleaseMask, &e); + XSync(blz.dpy, False); +} + +static Key * +match_keys(Key *k, ulong mod, KeyCode keycode, Bool seq) { + Key *ret = nil, *next; + + for(next = k->tnext; k; (k=next) && (next=k->tnext)) { + if(seq) + k = k->next; + if(k && (k->mod == mod) && (k->key == keycode)) { + k->tnext = ret; + ret = k; + } + } + return ret; +} + +static void +kpress_seq(Window w, Key *done) { + ulong mod; + KeyCode key; + Key *found; + + next_keystroke(&mod, &key); + found = match_keys(done, mod, key, True); + if((done->mod == mod) && (done->key == key)) + emulate_key_press(mod, key); /* double key */ + else { + if(!found) { + XBell(blz.dpy, 0); + } /* grabbed but not found */ + else if(!found->tnext && !found->next) + write_event("Key %s\n", found->name); + else + kpress_seq(w, found); + } +} + +void +kpress(Window w, ulong mod, KeyCode keycode) { + Key *k, *found; + + for(k=key; k; k=k->lnext) + k->tnext=k->lnext; + found = match_keys(key, mod, keycode, False); + if(!found) { + XBell(blz.dpy, 0); + } /* grabbed but not found */ + else if(!found->tnext && !found->next) + write_event("Key %s\n", found->name); + else { + XGrabKeyboard(blz.dpy, w, True, GrabModeAsync, GrabModeAsync, CurrentTime); + kpress_seq(w, found); + XUngrabKeyboard(blz.dpy, CurrentTime); + XSync(blz.dpy, False); + } +} + +void +update_keys() { + Key *k, *n; + char *l, *p; + + init_lock_keys(); + while((k = key)) { + key = key->lnext; + ungrab_key(k); + while((n = k)) { + k = k->next; + free(n); + } + } + for(l = p = def.keys; p && *p;) { + if(*p == '\n') { + *p = 0; + if((k = get_key(l))) + grab_key(k); + *p = '\n'; + l = ++p; + } + else + p++; + } + if(l < p && strlen(l)) { + if((k = get_key(l))) + grab_key(k); + } + XSync(blz.dpy, False); +} diff --git a/cmd/wmii/main.c b/cmd/wmii/main.c @@ -0,0 +1,583 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <X11/Xatom.h> +#include <X11/Xproto.h> +#include <X11/cursorfont.h> +#include <errno.h> +#include <locale.h> +#include <pwd.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include "wmii.h" + +static const char + version[] = "wmii - " VERSION ", ©2007 Kris Maglione\n"; + +static int (*x_error_handler) (Display *, XErrorEvent *); +static char *address, *ns_path; +static Bool check_other_wm; +static struct sigaction sa; +static struct passwd *passwd; +static int sleeperfd, sock; + +static void +usage() { + fatal("usage: wmii [-a <address>] [-r <wmiirc>] [-v]\n"); +} + +static void +scan_wins() { + int i; + uint num; + Window *wins; + XWindowAttributes wa; + Window d1, d2; + + if(XQueryTree(blz.dpy, blz.root, &d1, &d2, &wins, &num)) { + for(i = 0; i < num; i++) { + if(!XGetWindowAttributes(blz.dpy, wins[i], &wa)) + continue; + if(wa.override_redirect || XGetTransientForHint(blz.dpy, wins[i], &d1)) + continue; + if(wa.map_state == IsViewable) + manage_client(create_client(wins[i], &wa)); + } + for(i = 0; i < num; i++) { + if(!XGetWindowAttributes(blz.dpy, wins[i], &wa)) + continue; + if(XGetTransientForHint(blz.dpy, wins[i], &d1) + && wa.map_state == IsViewable) + manage_client(create_client(wins[i], &wa)); + } + } + if(wins) + XFree(wins); +} + +int +win_proto(Window w) { + Atom *protocols; + Atom real; + ulong nitems, extra; + int i, format, status, protos; + + status = XGetWindowProperty( + /* display */ blz.dpy, + /* window */ w, + /* property */ atom[WMProtocols], + /* offset */ 0L, + /* length */ 20L, + /* delete */ False, + /* req_type */ XA_ATOM, + /* type_ret */ &real, + /* format_ret */&format, + /* nitems_ret */&nitems, + /* extra_bytes */&extra, + /* prop_return */(uchar**)&protocols + ); + + if(status != Success || protocols == 0) { + return 0; + } + + if(nitems == 0) { + free(protocols); + return 0; + } + + protos = 0; + for(i = 0; i < nitems; i++) { + if(protocols[i] == atom[WMDelete]) + protos |= WM_PROTOCOL_DELWIN; + } + + free(protocols); + return protos; +} + +static void +ns_display() { + char *p, *disp; + + disp = getenv("DISPLAY"); + if(disp == nil) + fatal("DISPLAY is unset"); + + disp = estrdup(disp); + p = &disp[strlen(disp) - 2]; + if(strcmp(p, ".0") == 0) + *p = '\0'; + + ns_path = emalloc(strlen(disp) + strlen(user) + strlen("/tmp/ns..") + 1); + sprintf(ns_path, "/tmp/ns.%s.%s", user, disp); + + free(disp); +} + +static void +rmkdir(char *path, int mode) { + char *p, *q; + int ret; + char c; + + q = path + strlen(ns_path); + for(p = &path[1]; p <= q; p++) { + c = *p; + if((c == '/') || (c == '\0')) { + *p = '\0'; + ret = mkdir(path, mode); + if((ret == -1) && (errno != EEXIST)) + fatal("Can't create ns_path '%s':", path); + *p = c; + } + } +} + +static void +init_ns() { + struct stat st; + char *p; + + if(address && strncmp(address, "unix!", 5) == 0) { + ns_path = estrdup(&address[5]); + p = strrchr(ns_path, '/'); + if(p != nil) + p = '\0'; + } + else if((p = getenv("WMII_NS_PATH")) || (p = getenv("NAMESPACE"))) + ns_path = p; + else + ns_display(); + + if((ns_path[0] != '/') || (strlen(ns_path) == 0)) + fatal("Bad ns_path"); + + rmkdir(ns_path, 0700); + + if(stat(ns_path, &st)) + fatal("Can't stat ns_path '%s':", ns_path); + if(getuid() != st.st_uid) + fatal("ns_path '%s' exists but is not owned by you", + ns_path); + if(st.st_mode & 077) + fatal("ns_path '%s' exists, but has group or world permissions", + ns_path); +} + +static void +init_environment() { + if(address == nil) + address = getenv("WMII_ADDRESS"); + + init_ns(); + + if(address == nil) { + address = emalloc(strlen(ns_path) + strlen("unix!/wmii") + 1); + sprintf(address, "unix!%s/wmii", ns_path); + } + + setenv("WMII_NS_DIR", ns_path, True); + setenv("WMII_ADDRESS", address, True); +} + +static void +intern_atom(int ident, char *name) { + atom[ident] = XInternAtom(blz.dpy, name, False); +} + +static void +init_atoms() { + intern_atom(WMState, "WM_STATE"); + intern_atom(WMProtocols, "WM_PROTOCOLS"); + intern_atom(WMDelete, "WM_DELETE_WINDOW"); + intern_atom(NetSupported, "_NET_SUPPORTED"); + intern_atom(NetWMName, "_NET_WM_NAME"); + intern_atom(TagsAtom, "_WIN_TAGS"); + + XChangeProperty(blz.dpy, blz.root, atom[NetSupported], XA_ATOM, 32, + PropModeReplace, (uchar *)&atom[NetSupported], 2); +} + +static void +create_cursor(int ident, uint shape) { + cursor[ident] = XCreateFontCursor(blz.dpy, shape); +} + +static void +init_cursors() { + Pixmap pix; + XColor black, dummy; + + create_cursor(CurNormal, XC_left_ptr); + create_cursor(CurNECorner, XC_top_right_corner); + create_cursor(CurNWCorner, XC_top_left_corner); + create_cursor(CurSECorner, XC_bottom_right_corner); + create_cursor(CurSWCorner, XC_bottom_left_corner); + create_cursor(CurMove, XC_fleur); + create_cursor(CurInput, XC_xterm); + + XAllocNamedColor(blz.dpy, DefaultColormap(blz.dpy, blz.screen), + "black", &black, + &dummy); + pix = XCreateBitmapFromData(blz.dpy, blz.root, + (char[]){0}, 1, 1); + + cursor[CurInvisible] = XCreatePixmapCursor(blz.dpy, + pix, pix, + &black, &black, + 0, 0); + + XFreePixmap(blz.dpy, pix); +} + +static void +init_screen(WMScreen *screen) { + Window w; + int ret; + unsigned mask; + XGCValues gcv; + + gcv.subwindow_mode = IncludeInferiors; + gcv.function = GXxor; + gcv.foreground = def.normcolor.bg; + gcv.plane_mask = AllPlanes; + gcv.graphics_exposures = False; + xorgc = XCreateGC(blz.dpy, blz.root, + GCForeground + | GCGraphicsExposures + | GCFunction + | GCSubwindowMode + | GCPlaneMask, + &gcv); + + screen->rect.x = screen->rect.y = 0; + screen->rect.width = DisplayWidth(blz.dpy, blz.screen); + screen->rect.height = DisplayHeight(blz.dpy, blz.screen); + def.snap = screen->rect.height / 63; + + sel_screen = XQueryPointer(blz.dpy, blz.root, + &w, &w, + &ret, &ret, &ret, &ret, + &mask); +} + +/* + * There's no way to check accesses to destroyed windows, thus + * those cases are ignored (especially on UnmapNotify's). + * Other types of errors call Xlib's default error handler, which + * calls exit(). + */ +int +wmii_error_handler(Display *dpy, XErrorEvent *error) { + if(check_other_wm) + fatal("another window manager is already running"); + + if(error->error_code == BadWindow + ||(error->request_code == X_SetInputFocus + && error->error_code == BadMatch) + ||(error->request_code == X_PolyText8 + && error->error_code == BadDrawable) + ||(error->request_code == X_PolyFillRectangle + && error->error_code == BadDrawable) + ||(error->request_code == X_PolySegment + && error->error_code == BadDrawable) + ||(error->request_code == X_ConfigureWindow + && error->error_code == BadMatch) + ||(error->request_code == X_GrabKey + && error->error_code == BadAccess)) + return 0; + + fprintf(stderr, "wmii: fatal error: Xrequest code=%d, Xerror code=%d\n", + error->request_code, error->error_code); + return x_error_handler(blz.dpy, error); /* calls exit() */ +} + +static void +cleanup() { + Client *c; + + for(c=client; c; c=c->next) + reparent_client(c, blz.root, c->sel->rect.x, c->sel->rect.y); + XSync(blz.dpy, False); +} + +static void +cleanup_handler(int signal) { + sa.sa_handler = SIG_DFL; + sigaction(signal, &sa, nil); + + switch(signal) { + case SIGINT: + srv.running = False; + break; + default: + cleanup(); + XCloseDisplay(blz.dpy); + raise(signal); + break; + } +} + +static void +init_traps() { + char buf[1]; + int fd[2]; + + if(pipe(fd) != 0) + fatal("Can't pipe():"); + + switch(fork()) { + case -1: + fatal("Can't fork():"); + break; /* not reached */ + case 0: + close(fd[1]); + close(ConnectionNumber(blz.dpy)); + setsid(); + + blz.dpy = XOpenDisplay(0); + if(!blz.dpy) + fatal("Can't open display"); + + /* Wait for parent to exit */ + read(fd[0], buf, 1); + + XSetInputFocus(blz.dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XCloseDisplay(blz.dpy); + exit(0); + default: + break; + } + + close(fd[0]); + sleeperfd = fd[1]; + + sa.sa_flags = 0; + sa.sa_handler = cleanup_handler; + sigaction(SIGINT, &sa, nil); + sigaction(SIGTERM, &sa, nil); + sigaction(SIGQUIT, &sa, nil); + sigaction(SIGHUP, &sa, nil); +} + +static void +spawn_command(const char *cmd) { + char *shell, *p; + pid_t pid; + int status; + + /* Double fork hack */ + switch(pid = fork()) { + case -1: + fatal("Can't fork:"); + break; /* Not reached */ + case 0: + switch(fork()) { + case -1: + fatal("Can't fork:"); + break; /* Not reached */ + case 0: + if(setsid() == -1) + fatal("Can't setsid:"); + close(sock); + close(ConnectionNumber(blz.dpy)); + + shell = passwd->pw_shell; + if(shell[0] != '/') + fatal("Shell is not an absolute path: %s", shell); + + /* Run through the user's shell as a login shell */ + p = malloc((strlen(shell) + 2)); + sprintf(p, "-%s", strrchr(shell, '/') + 1); + + execl(shell, p, "-c", cmd, nil); + fatal("Can't exec '%s':", cmd); + break; /* Not reached */ + default: + exit(0); + break; /* Not reached */ + } + default: + waitpid(pid, &status, 0); + if(status != 0) + exit(1); /* Error already printed */ + break; + } +} + +void +check_9pcon(IxpConn *c) { + serve_9pcon(c); + check_x_event(nil); +} + +int +main(int argc, char *argv[]) { + char *wmiirc, *errstr; + WMScreen *s; + XSetWindowAttributes wa; + int i; + + passwd = getpwuid(getuid()); + user = estrdup(passwd->pw_name); + wmiirc = "wmiistartrc"; + + /* command line args */ + ARGBEGIN{ + case 'v': + printf("%s", version); + exit(0); + case 'V': + verbose = True; + break; + case 'a': + address = EARGF(usage()); + break; + case 'r': + wmiirc = EARGF(usage()); + break; + default: + usage(); + break; + }ARGEND; + + setlocale(LC_CTYPE, ""); + starting = True; + + blz.dpy = XOpenDisplay(0); + if(!blz.dpy) + fatal("Can't open display"); + blz.screen = DefaultScreen(blz.dpy); + blz.root = RootWindow(blz.dpy, blz.screen); + + check_other_wm = True; + x_error_handler = XSetErrorHandler(wmii_error_handler); + XSelectInput(blz.dpy, blz.root, SubstructureRedirectMask | EnterWindowMask); + XSync(blz.dpy, False); + check_other_wm = False; + + init_environment(); + init_traps(); + + errstr = nil; + sock = ixp_dial(address); + if(sock < 0) + fatal("Can't create socket '%s': %s", address, errstr); + + if(wmiirc) + spawn_command(wmiirc); + + ixp_listen(&srv, sock, &p9srv, check_9pcon, nil); + ixp_listen(&srv, ConnectionNumber(blz.dpy), nil, check_x_event, nil); + + view = nil; + client = nil; + key = nil; + + def.font.fontstr = estrdup(BLITZ_FONT); + def.border = 1; + def.colmode = Coldefault; + + def.mod = Mod1Mask; + strncpy(def.grabmod, "Mod1", sizeof(def.grabmod)); + + strncpy(def.focuscolor.colstr, BLITZ_FOCUSCOLORS, sizeof(def.focuscolor.colstr)); + strncpy(def.normcolor.colstr, BLITZ_NORMCOLORS, sizeof(def.normcolor.colstr)); + loadcolor(&blz, &def.focuscolor); + loadcolor(&blz, &def.normcolor); + + init_atoms(); + init_cursors(); + loadfont(&blz, &def.font); + init_lock_keys(); + + num_screens = 1; + screens = emallocz(num_screens * sizeof(*screens)); + for(i = 0; i < num_screens; i++) { + s = &screens[i]; + init_screen(s); + pmap = XCreatePixmap( + /* display */ blz.dpy, + /* drawable */ blz.root, + /* width */ s->rect.width, + /* height */ s->rect.height, + /* depth */ DefaultDepth(blz.dpy, blz.screen) + ); + wa.event_mask = + SubstructureRedirectMask + | EnterWindowMask + | LeaveWindowMask + | FocusChangeMask; + wa.cursor = cursor[CurNormal]; + XChangeWindowAttributes(blz.dpy, blz.root, CWEventMask | CWCursor, &wa); + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = + ExposureMask + | ButtonReleaseMask + | FocusChangeMask + | SubstructureRedirectMask + | SubstructureNotifyMask; + s->brect = s->rect; + s->brect.height = labelh(&def.font); + s->brect.y = s->rect.height - s->brect.height; + s->barwin = XCreateWindow( + /* display */ blz.dpy, + /* parent */ RootWindow(blz.dpy, blz.screen), + /* x */ s->brect.x, + /* y */ s->brect.y, + /* width */ s->brect.width, + /* height */ s->brect.height, + /*border_width*/0, + /* depth */ DefaultDepth(blz.dpy, blz.screen), + /* class */ CopyFromParent, + /* visual */ DefaultVisual(blz.dpy, blz.screen), + /* valuemask */ CWOverrideRedirect | CWBackPixmap | CWEventMask, + /* attrubutes */&wa + ); + XSync(blz.dpy, False); + s->bbrush.blitz = &blz; + s->bbrush.gc = XCreateGC(blz.dpy, s->barwin, 0, 0); + s->bbrush.drawable = pmap; + s->bbrush.rect = s->brect; + s->bbrush.rect.x = 0; + s->bbrush.rect.y = 0; + s->bbrush.color = def.normcolor; + s->bbrush.font = &def.font; + s->bbrush.border = 1; + draw_bar(s); + XMapRaised(blz.dpy, s->barwin); + } + + screen = &screens[0]; + screen->focus = nil; + XSetInputFocus(blz.dpy, screen->barwin, RevertToParent, CurrentTime); + + scan_wins(); + starting = False; + update_views(); + if(view) + write_event("FocusTag %s\n", screen->sel->name); + + check_x_event(nil); + errstr = ixp_serverloop(&srv); + if(errstr) + + cleanup(); + XCloseDisplay(blz.dpy); + ixp_server_close(&srv); + close(sleeperfd); + + if(execstr) + execl("/bin/sh", "sh", "-c", execstr, nil); + + if(errstr) + fatal("%s", errstr); + return 0; +} diff --git a/cmd/wmii/mouse.c b/cmd/wmii/mouse.c @@ -0,0 +1,490 @@ +/* Copyright ©2006 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <stdlib.h> +#include <string.h> +#include "wmii.h" + +enum { + ButtonMask = + ButtonPressMask | ButtonReleaseMask, + MouseMask = + ButtonMask | PointerMotionMask +}; + +static void +rect_morph_xy(XRectangle *rect, int dx, int dy, BlitzAlign *mask) { + BlitzAlign new_mask = 0; + if(*mask & NORTH) { + if(rect->height - dy >= 0 || *mask & SOUTH) { + rect->y += dy; + rect->height -= dy; + } + else { + rect->y += rect->height; + rect->height = dy - rect->height; + new_mask ^= NORTH|SOUTH; + } + } + if(*mask & SOUTH) { + if(rect->height + dy >= 0 || *mask & NORTH) + rect->height += dy; + else { + rect->height = -dy - rect->height; + rect->y -= rect->height; + new_mask ^= NORTH|SOUTH; + } + } + if(*mask & EAST) { + if(rect->width + dx >= 0 || *mask & WEST) + rect->width += dx; + else { + rect->width = -dx - rect->width; + rect->x -= rect->width; + new_mask ^= EAST|WEST; + } + } + if(*mask & WEST) { + if(rect->width - dx >= 0 || *mask & EAST) { + rect->x += dx; + rect->width -= dx; + } + else { + rect->x += rect->width; + rect->width = dx - rect->width; + new_mask ^= EAST|WEST; + } + } + *mask ^= new_mask; +} + +typedef struct { + XRectangle *rects; + int num; + int x1, y1, x2, y2; + BlitzAlign mask; + int *delta; +} SnapArgs; + +static void +snap_line(SnapArgs *a) { + int i, t_xy; + + /* horizontal */ + if(a->y1 == a->y2 && (a->mask & (NORTH|SOUTH))) { + for(i=0; i < a->num; i++) { + if(!(r_east(&a->rects[i]) < a->x1) || + (a->rects[i].x > a->x2)) { + + if(abs(a->rects[i].y - a->y1) <= abs(*a->delta)) + *a->delta = a->rects[i].y - a->y1; + + t_xy = r_south(&a->rects[i]); + if(abs(t_xy - a->y1) < abs(*a->delta)) + *a->delta = t_xy - a->y1; + } + } + } + else if (a->mask & (EAST|WEST)) { + /* This is the same as above, tr/xy/yx/, + * s/width/height/, s/height/width/ */ + for(i=0; i < a->num; i++) { + if(!(r_south(&a->rects[i]) < a->y1) || + (a->rects[i].y > a->y2)) { + + if(abs(a->rects[i].x - a->x1) <= abs(*a->delta)) + *a->delta = a->rects[i].x - a->x1; + + t_xy = r_east(&a->rects[i]); + if(abs(t_xy - a->x1) < abs(*a->delta)) + *a->delta = t_xy - a->x1; + } + } + } +} + +BlitzAlign +snap_rect(XRectangle *rects, int num, XRectangle *current, BlitzAlign *mask, int snap) { + SnapArgs a = { rects, num, 0, 0, 0, 0, *mask, nil }; + int dx, dy; + BlitzAlign ret; + + dx = dy = snap + 1; + + a.x1 = current->x; + a.x2 = r_east(current); + a.delta = &dy; + if(*mask & NORTH) { + a.y2 = a.y1 = current->y; + snap_line(&a); + } + if(*mask & SOUTH) { + a.y2 = a.y1 = r_south(current); + snap_line(&a); + } + + a.y1 = current->y; + a.y2 = r_south(current); + a.delta = &dx; + if(*mask & EAST) { + a.x1 = a.x2 = r_east(current); + snap_line(&a); + } + if(*mask & WEST) { + a.x1 = a.x2 = current->x; + snap_line(&a); + } + + rect_morph_xy(current, + abs(dx) <= snap ? dx : 0, + abs(dy) <= snap ? dy : 0, + mask); + + ret = *mask; + if(abs(dx) <= snap) + ret ^= EAST|WEST; + if(abs(dy) <= snap) + ret ^= NORTH|SOUTH; + return ret ^ CENTER; +} + +static void +draw_xor_border(XRectangle *r) { + XRectangle xor; + + xor = *r; + xor.x += 2; + xor.y += 2; + xor.width = xor.width > 4 ? xor.width - 4 : 0; + xor.height = xor.height > 4 ? xor.height - 4 : 0; + + XSetLineAttributes(blz.dpy, xorgc, 1, LineSolid, CapNotLast, JoinMiter); + XSetForeground(blz.dpy, xorgc, def.focuscolor.bg); + if(xor.height > 4 && xor.width > 2) + XDrawLine(blz.dpy, blz.root, xorgc, + xor.x + 2, + xor.y + xor.height / 2, + r_east(&xor) - 2, + xor.y + xor.height / 2); + if(xor.width > 4 && xor.height > 2) + XDrawLine(blz.dpy, blz.root, xorgc, + xor.x + xor.width / 2, + xor.y + 2, + xor.x + xor.width / 2, + r_south(&xor) - 2); + XSetLineAttributes(blz.dpy, xorgc, 4, LineSolid, CapNotLast, JoinMiter); + XDrawRectangles(blz.dpy, blz.root, xorgc, &xor, 1); +} + +static void +find_droppoint(Frame *frame, int x, int y, XRectangle *rect, Bool do_move) { + View *v; + Area *a, *a_prev; + Frame *f, *f_close; + + v = frame->view; + rect->y = 0; + rect->height = screen->rect.height - screen->brect.height; + + a_prev = v->area; + for(a = a_prev->next; a && a->next; a = a->next) { + if(x < r_east(&a->rect)) + break; + a_prev = a; + } + if(x < (a->rect.x + labelh(&def.font))) { + rect->x = a->rect.x - 4; + rect->width = 8; + + if(do_move) { + a = new_column(v, a_prev, 0); + send_to_area(a, frame); + focus(frame->client, False); + } + return; + } + if(x > (r_east(&a->rect) - labelh(&def.font))) { + rect->x = r_east(&a->rect) - 4; + rect->width = 8; + + if(do_move) { + a = new_column(v, a, 0); + send_to_area(a, frame); + focus(frame->client, False); + } + return; + } + + rect->x = a->rect.x; + rect->width = a->rect.width; + + f_close = nil; + for(f = a->frame; f; f = f->anext) { + if(y < f->rect.y) + break; + if(y < r_south(&f->rect)) + break; + f_close = f; + } + if(f == nil) + f = f_close; + if(y < (f->rect.y + labelh(&def.font))) { + rect->y = f->rect.y; + rect->height = 2; + if(f_close) { + rect->y = r_south(&f_close->rect); + rect->height = f->rect.y - rect->y; + } + if(do_move) + goto do_move; + return; + } + if(y > r_south(&f->rect) - labelh(&def.font)) { + rect->y = r_south(&f->rect); + rect->height = (screen->rect.height - labelh(&def.font) - rect->y); + if(f->anext) + rect->height = (f->anext->rect.y - rect->y); + if(do_move) + goto do_move; + return; + } + + *rect = f->rect; + if(do_move) { + swap_frames(frame, f); + focus(frame->client, False); + } + return; + +do_move: + if(frame == f) + return; + if(a != frame->area) + send_to_area(a, frame); + remove_frame(frame); + insert_frame(f, frame, False); + arrange_column(f->area, False); + focus(frame->client, True); +} + +static void +do_managed_move(Client *c) { + XRectangle frect, ofrect; + Window dummy; + XEvent ev; + Frame *f; + uint di; + int x, y, i; + + focus(c, False); + f = c->sel; + + XSync(blz.dpy, False); + if(XGrabPointer(blz.dpy, c->framewin, False, MouseMask, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove], CurrentTime) != GrabSuccess) + return; + XGrabServer(blz.dpy); + + XQueryPointer(blz.dpy, blz.root, &dummy, &dummy, &i, &i, &x, &y, &di); + + find_droppoint(f, x, y, &frect, False); + draw_xor_border(&frect); + for(;;) { + XMaskEvent(blz.dpy, MouseMask | ExposureMask, &ev); + switch (ev.type) { + case ButtonRelease: + draw_xor_border(&frect); + + find_droppoint(f, x, y, &frect, True); + + XUngrabServer(blz.dpy); + XUngrabPointer(blz.dpy, CurrentTime); + XSync(blz.dpy, False); + return; + case MotionNotify: + ofrect = frect; + x = ev.xmotion.x_root; + y = ev.xmotion.y_root; + + find_droppoint(f, x, y, &frect, False); + + if(memcmp(&frect, &ofrect, sizeof(frect))) { + draw_xor_border(&ofrect); + draw_xor_border(&frect); + } + break; + case Expose: + (handler[Expose])(&ev); + break; + default: break; + } + } +} + +void +do_mouse_resize(Client *c, Bool grabbox, BlitzAlign align) { + BlitzAlign grav; + Window dummy; + Cursor cur; + XEvent ev; + XRectangle *rects, ofrect, frect, origin; + int snap, dx, dy, pt_x, pt_y, hr_x, hr_y, i; + uint num, di; + Bool floating; + float rx, ry; + Frame *f; + + f = c->sel; + floating = f->area->floating; + origin = frect = f->rect; + cur = cursor_of_quad(align); + if(floating) { + rects = rects_of_view(f->area->view, &num, (grabbox ? c->frame : nil)); + snap = screen->rect.height / 66; + }else{ + rects = nil; + snap = 0; + } + + if(align == CENTER) { + if(!grabbox) + cur = cursor[CurInvisible]; + if(!floating) { + do_managed_move(c); + return; + } + } + + XQueryPointer(blz.dpy, c->framewin, &dummy, &dummy, &i, &i, &pt_x, &pt_y, &di); + rx = (float)pt_x / frect.width; + ry = (float)pt_y / frect.height; + + if(XGrabPointer( + /* display */ blz.dpy, + /* window */ c->framewin, + /* owner_events */ False, + /* event_mask */ MouseMask, + /* pointer_mode */ GrabModeAsync, + /* keyboard_mode */ GrabModeAsync, + /* confine_to */ None, + /* cursor */ cur, + /* time */ CurrentTime + ) != GrabSuccess) + return; + + XQueryPointer(blz.dpy, blz.root, &dummy, &dummy, &i, &i, &pt_x, &pt_y, &di); + + if(align != CENTER) { + hr_x = dx = frect.width / 2; + hr_y = dy = frect.height / 2; + if(align&NORTH) dy -= hr_y; + if(align&SOUTH) dy += hr_y; + if(align&EAST) dx += hr_x; + if(align&WEST) dx -= hr_x; + XTranslateCoordinates(blz.dpy, c->framewin, blz.root, dx, dy, + &pt_x, &pt_y, &dummy); + XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, pt_x, pt_y); + } + else if(!grabbox) { + hr_x = screen->rect.width / 2; + hr_y = screen->rect.height / 2; + XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, hr_x, hr_y); + flushevents(PointerMotionMask, False); + } + + + XSync(blz.dpy, False); + if(!grabbox) { + XGrabServer(blz.dpy); + draw_xor_border(&frect); + }else + unmap_client(c, IconicState); + + for(;;) { + XMaskEvent(blz.dpy, MouseMask | ExposureMask, &ev); + switch (ev.type) { + case ButtonRelease: + if(!grabbox) + draw_xor_border(&frect); + + if(!floating) + resize_column(c, &frect); + else + resize_client(c, &frect); + + if(!grabbox) { + if(align != CENTER) + XTranslateCoordinates(blz.dpy, c->framewin, blz.root, + (frect.width * rx), (frect.height * ry), + &pt_x, &pt_y, &dummy); + if(pt_y > screen->brect.y) + pt_y = screen->brect.y - 1; + XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, pt_x, pt_y); + XUngrabServer(blz.dpy); + }else + map_client(c); + + if(rects) + free(rects); + + XUngrabPointer(blz.dpy, CurrentTime); + XSync(blz.dpy, False); + return; + case MotionNotify: + ofrect = frect; + dx = ev.xmotion.x_root; + dy = ev.xmotion.y_root; + + if(align == CENTER && !grabbox) { + if(dx == hr_x && dy == hr_y) + continue; + XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, hr_x, hr_y); + dx -= hr_x; + dy -= hr_y; + }else{ + dx -= pt_x; + dy -= pt_y; + } + pt_x += dx; + pt_y += dy; + + rect_morph_xy(&origin, dx, dy, &align); + if(align != CENTER) + check_frame_constraints(&origin); + frect = origin; + + if(floating) + grav = snap_rect(rects, num, &frect, &align, snap); + else + grav = align ^ CENTER; + + apply_sizehints(c, &frect, floating, True, grav); + + if(grabbox) { + XMoveWindow(blz.dpy, c->framewin, frect.x, frect.y); + XSync(blz.dpy, False); + } else { + draw_xor_border(&ofrect); + draw_xor_border(&frect); + } + break; + case Expose: + (handler[Expose])(&ev); + break; + default: + break; + } + } +} + +void +grab_button(Window w, uint button, ulong mod) { + XGrabButton(blz.dpy, button, mod, w, False, ButtonMask, + GrabModeSync, GrabModeSync, None, None); + if((mod != AnyModifier) && num_lock_mask) { + XGrabButton(blz.dpy, button, mod | num_lock_mask, w, False, ButtonMask, + GrabModeSync, GrabModeAsync, None, None); + XGrabButton(blz.dpy, button, mod | num_lock_mask | LockMask, w, False, + ButtonMask, GrabModeSync, GrabModeAsync, None, None); + } +} diff --git a/cmd/wmii/printevent.c b/cmd/wmii/printevent.c @@ -0,0 +1,997 @@ +/* + * Original code posted to comp.sources.x + * Modifications by Russ Cox <rsc@swtch.com>. + */ + +/* +Path: uunet!wyse!mikew +From: mikew@wyse.wyse.com (Mike Wexler) +Newsgroups: comp.sources.x +Subject: v02i056: subroutine to print events in human readable form, Part01/01 +Message-ID: <1935@wyse.wyse.com> +Date: 22 Dec 88 19:28:25 GMT +Organization: Wyse Technology, San Jose +Lines: 1093 +Approved: mikew@wyse.com + +Submitted-by: richsun!darkstar!ken +Posting-number: Volume 2, Issue 56 +Archive-name: showevent/part01 + + +There are times during debugging when it would be real useful to be able to +print the fields of an event in a human readable form. Too many times I found +myself scrounging around in section 8 of the Xlib manual looking for the valid +fields for the events I wanted to see, then adding printf's to display the +numeric values of the fields, and then scanning through X.h trying to decode +the cryptic detail and state fields. After playing with xev, I decided to +write a couple of standard functions that I could keep in a library and call +on whenever I needed a little debugging verbosity. The first function, +GetType(), is useful for returning the string representation of the type of +an event. The second function, ShowEvent(), is used to display all the fields +of an event in a readable format. The functions are not complicated, in fact, +they are mind-numbingly boring - but that's just the point nobody wants to +spend the time writing functions like this, they just want to have them when +they need them. + +A simple, sample program is included which does little else but to demonstrate +the use of these two functions. These functions have saved me many an hour +during debugging and I hope you find some benefit to these. If you have any +comments, suggestions, improvements, or if you find any blithering errors you +can get it touch with me at the following location: + + ken@richsun.UUCP +*/ + +#include <stdio.h> +#include <X11/Intrinsic.h> +#include <X11/Xproto.h> +#include "wmii.h" +#include "printevent.h" + +static char* sep = " "; + +/******************************************************************************/ +/**** Miscellaneous routines to convert values to their string equivalents ****/ +/******************************************************************************/ + +/* Returns the string equivalent of a boolean parameter */ +static char* +TorF(int bool) +{ + switch (bool) { + case True: + return ("True"); + + case False: + return ("False"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a property notify state */ +static char* +PropertyState(int state) +{ + switch (state) { + case PropertyNewValue: + return ("PropertyNewValue"); + + case PropertyDelete: + return ("PropertyDelete"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a visibility notify state */ +static char* +VisibilityState(int state) +{ + switch (state) { + case VisibilityUnobscured: + return ("VisibilityUnobscured"); + + case VisibilityPartiallyObscured: + return ("VisibilityPartiallyObscured"); + + case VisibilityFullyObscured: + return ("VisibilityFullyObscured"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a timestamp */ +static char* +ServerTime(Time time) +{ + ulong msec; + ulong sec; + ulong min; + ulong hr; + ulong day; + static char buffer[32]; + + msec = time % 1000; + time /= 1000; + sec = time % 60; + time /= 60; + min = time % 60; + time /= 60; + hr = time % 24; + time /= 24; + day = time; + +if(0) + sprintf(buffer, "%lu day%s %02lu:%02lu:%02lu.%03lu", + day, day == 1 ? "" : "(s)", hr, min, sec, msec); + + sprintf(buffer, "%lud%luh%lum%lu.%03lds", day, hr, min, sec, msec); + return (buffer); +} + +/* Simple structure to ease the interpretation of masks */ +typedef struct MaskType MaskType; +struct MaskType +{ + uint value; + char *string; +}; + +/* Returns the string equivalent of a mask of buttons and/or modifier keys */ +static char* +ButtonAndOrModifierState(uint state) +{ + static char buffer[256]; + static MaskType masks[] = { + {Button1Mask, "Button1Mask"}, + {Button2Mask, "Button2Mask"}, + {Button3Mask, "Button3Mask"}, + {Button4Mask, "Button4Mask"}, + {Button5Mask, "Button5Mask"}, + {ShiftMask, "ShiftMask"}, + {LockMask, "LockMask"}, + {ControlMask, "ControlMask"}, + {Mod1Mask, "Mod1Mask"}, + {Mod2Mask, "Mod2Mask"}, + {Mod3Mask, "Mod3Mask"}, + {Mod4Mask, "Mod4Mask"}, + {Mod5Mask, "Mod5Mask"}, + }; + int num_masks = sizeof(masks) / sizeof(MaskType); + int i; + Boolean first = True; + + buffer[0] = 0; + + for (i = 0; i < num_masks; i++) + if (state & masks[i].value) { + if (first) { + first = False; + strcpy(buffer, masks[i].string); + } else { + strcat(buffer, " | "); + strcat(buffer, masks[i].string); + } + } + return (buffer); +} + +/* Returns the string equivalent of a mask of configure window values */ +static char* +ConfigureValueMask(uint valuemask) +{ + static char buffer[256]; + static MaskType masks[] = { + {CWX, "CWX"}, + {CWY, "CWY"}, + {CWWidth, "CWWidth"}, + {CWHeight, "CWHeight"}, + {CWBorderWidth, "CWBorderWidth"}, + {CWSibling, "CWSibling"}, + {CWStackMode, "CWStackMode"}, + }; + int num_masks = sizeof(masks) / sizeof(MaskType); + int i; + Boolean first = True; + + buffer[0] = 0; + + for (i = 0; i < num_masks; i++) + if (valuemask & masks[i].value) { + if (first) { + first = False; + strcpy(buffer, masks[i].string); + } else { + strcat(buffer, " | "); + strcat(buffer, masks[i].string); + } + } + + return (buffer); +} + +/* Returns the string equivalent of a motion hint */ +static char* +IsHint(char is_hint) +{ + switch (is_hint) { + case NotifyNormal: + return ("NotifyNormal"); + + case NotifyHint: + return ("NotifyHint"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of an id or the value "None" */ +static char* +MaybeNone(int value) +{ + static char buffer[16]; + + if (value == None) + return ("None"); + else { + sprintf(buffer, "0x%x", value); + return (buffer); + } +} + +/* Returns the string equivalent of a colormap state */ +static char* +ColormapState(int state) +{ + switch (state) { + case ColormapInstalled: + return ("ColormapInstalled"); + + case ColormapUninstalled: + return ("ColormapUninstalled"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a crossing detail */ +static char* +CrossingDetail(int detail) +{ + switch (detail) { + case NotifyAncestor: + return ("NotifyAncestor"); + + case NotifyInferior: + return ("NotifyInferior"); + + case NotifyVirtual: + return ("NotifyVirtual"); + + case NotifyNonlinear: + return ("NotifyNonlinear"); + + case NotifyNonlinearVirtual: + return ("NotifyNonlinearVirtual"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a focus change detail */ +static char* +FocusChangeDetail(int detail) +{ + switch (detail) { + case NotifyAncestor: + return ("NotifyAncestor"); + + case NotifyInferior: + return ("NotifyInferior"); + + case NotifyVirtual: + return ("NotifyVirtual"); + + case NotifyNonlinear: + return ("NotifyNonlinear"); + + case NotifyNonlinearVirtual: + return ("NotifyNonlinearVirtual"); + + case NotifyPointer: + return ("NotifyPointer"); + + case NotifyPointerRoot: + return ("NotifyPointerRoot"); + + case NotifyDetailNone: + return ("NotifyDetailNone"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a configure detail */ +static char* +ConfigureDetail(int detail) +{ + switch (detail) { + case Above: + return ("Above"); + + case Below: + return ("Below"); + + case TopIf: + return ("TopIf"); + + case BottomIf: + return ("BottomIf"); + + case Opposite: + return ("Opposite"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a grab mode */ +static char* +GrabMode(int mode) +{ + switch (mode) { + case NotifyNormal: + return ("NotifyNormal"); + + case NotifyGrab: + return ("NotifyGrab"); + + case NotifyUngrab: + return ("NotifyUngrab"); + + case NotifyWhileGrabbed: + return ("NotifyWhileGrabbed"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a mapping request */ +static char* +MappingRequest(int request) +{ + switch (request) { + case MappingModifier: + return ("MappingModifier"); + + case MappingKeyboard: + return ("MappingKeyboard"); + + case MappingPointer: + return ("MappingPointer"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a stacking order place */ +static char* +Place(int place) +{ + switch (place) { + case PlaceOnTop: + return ("PlaceOnTop"); + + case PlaceOnBottom: + return ("PlaceOnBottom"); + + default: + return ("?"); + } +} + +/* Returns the string equivalent of a major code */ +static char* +MajorCode(int code) +{ + static char buffer[32]; + + switch (code) { + case X_CopyArea: + return ("X_CopyArea"); + + case X_CopyPlane: + return ("X_CopyPlane"); + + default: + sprintf(buffer, "0x%x", code); + return (buffer); + } +} + +/* Returns the string equivalent the keycode contained in the key event */ +static char* +Keycode(XKeyEvent *ev) +{ + static char buffer[256]; + KeySym keysym_str; + char *keysym_name; + char string[256]; + + XLookupString(ev, string, 64, &keysym_str, NULL); + + if (keysym_str == NoSymbol) + keysym_name = "NoSymbol"; + else if (!(keysym_name = XKeysymToString(keysym_str))) + keysym_name = "(no name)"; + sprintf(buffer, "%u (keysym 0x%x \"%s\")", + (int)ev->keycode, (int)keysym_str, keysym_name); + return (buffer); +} + +/* Returns the string equivalent of an atom or "None"*/ +static char* +AtomName(Display *dpy, Atom atom) +{ + static char buffer[256]; + char *atom_name; + + if (atom == None) + return ("None"); + + atom_name = XGetAtomName(dpy, atom); + strncpy(buffer, atom_name, 256); + XFree(atom_name); + return (buffer); +} + +/******************************************************************************/ +/**** Routines to print out readable values for the field of various events ***/ +/******************************************************************************/ + +static void +VerbMotion(XMotionEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); + fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); + fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "x_root=%d y_root=%d%s", ev->x_root, ev->y_root, sep); + fprintf(stderr, "state=%s%s", ButtonAndOrModifierState(ev->state), sep); + fprintf(stderr, "is_hint=%s%s", IsHint(ev->is_hint), sep); + fprintf(stderr, "same_screen=%s\n", TorF(ev->same_screen)); +} + +static void +VerbButton(XButtonEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); + fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); + fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "x_root=%d y_root=%d%s", ev->x_root, ev->y_root, sep); + fprintf(stderr, "state=%s%s", ButtonAndOrModifierState(ev->state), sep); + fprintf(stderr, "button=%s%s", ButtonAndOrModifierState(ev->button), sep); + fprintf(stderr, "same_screen=%s\n", TorF(ev->same_screen)); +} + +static void +VerbColormap(XColormapEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "colormap=%s%s", MaybeNone(ev->colormap), sep); + fprintf(stderr, "new=%s%s", TorF(ev->new), sep); + fprintf(stderr, "state=%s\n", ColormapState(ev->state)); +} + +static void +VerbCrossing(XCrossingEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); + fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); + fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "x_root=%d y_root=%d%s", ev->x_root, ev->y_root, sep); + fprintf(stderr, "mode=%s%s", GrabMode(ev->mode), sep); + fprintf(stderr, "detail=%s%s", CrossingDetail(ev->detail), sep); + fprintf(stderr, "same_screen=%s%s", TorF(ev->same_screen), sep); + fprintf(stderr, "focus=%s%s", TorF(ev->focus), sep); + fprintf(stderr, "state=%s\n", ButtonAndOrModifierState(ev->state)); +} + +static void +VerbExpose(XExposeEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); + fprintf(stderr, "count=%d\n", ev->count); +} + +static void +VerbGraphicsExpose(XGraphicsExposeEvent *ev) +{ + fprintf(stderr, "drawable=0x%x%s", (int)ev->drawable, sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); + fprintf(stderr, "major_code=%s%s", MajorCode(ev->major_code), sep); + fprintf(stderr, "minor_code=%d\n", ev->minor_code); +} + +static void +VerbNoExpose(XNoExposeEvent *ev) +{ + fprintf(stderr, "drawable=0x%x%s", (int)ev->drawable, sep); + fprintf(stderr, "major_code=%s%s", MajorCode(ev->major_code), sep); + fprintf(stderr, "minor_code=%d\n", ev->minor_code); +} + +static void +VerbFocus(XFocusChangeEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "mode=%s%s", GrabMode(ev->mode), sep); + fprintf(stderr, "detail=%s\n", FocusChangeDetail(ev->detail)); +} + +static void +VerbKeymap(XKeymapEvent *ev) +{ + int i; + + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "key_vector="); + for (i = 0; i < 32; i++) + fprintf(stderr, "%02x", ev->key_vector[i]); + fprintf(stderr, "\n"); +} + +static void +VerbKey(XKeyEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); + if(ev->subwindow) + fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); + fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); + fprintf(stderr, "[%d,%d]%s", ev->x, ev->y, sep); + fprintf(stderr, "root=[%d,%d]%s", ev->x_root, ev->y_root, sep); + if(ev->state) + fprintf(stderr, "state=%s%s", ButtonAndOrModifierState(ev->state), sep); + fprintf(stderr, "keycode=%s%s", Keycode(ev), sep); + if(!ev->same_screen) + fprintf(stderr, "!same_screen"); + fprintf(stderr, "\n"); + return; + + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); + fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); + fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "x_root=%d y_root=%d%s", ev->x_root, ev->y_root, sep); + fprintf(stderr, "state=%s%s", ButtonAndOrModifierState(ev->state), sep); + fprintf(stderr, "keycode=%s%s", Keycode(ev), sep); + fprintf(stderr, "same_screen=%s\n", TorF(ev->same_screen)); +} + +static void +VerbProperty(XPropertyEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "atom=%s%s", AtomName(ev->display, ev->atom), sep); + fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); + fprintf(stderr, "state=%s\n", PropertyState(ev->state)); +} + +static void +VerbResizeRequest(XResizeRequestEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "width=%d height=%d\n", ev->width, ev->height); +} + +static void +VerbCirculate(XCirculateEvent *ev) +{ + fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "place=%s\n", Place(ev->place)); +} + +static void +VerbConfigure(XConfigureEvent *ev) +{ + fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); + fprintf(stderr, "border_width=%d%s", ev->border_width, sep); + fprintf(stderr, "above=%s%s", MaybeNone(ev->above), sep); + fprintf(stderr, "override_redirect=%s\n", TorF(ev->override_redirect)); +} + +static void +VerbCreateWindow(XCreateWindowEvent *ev) +{ + fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); + fprintf(stderr, "border_width=%d%s", ev->border_width, sep); + fprintf(stderr, "override_redirect=%s\n", TorF(ev->override_redirect)); +} + +static void +VerbDestroyWindow(XDestroyWindowEvent *ev) +{ + fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); + fprintf(stderr, "window=0x%x\n", (int)ev->window); +} + +static void +VerbGravity(XGravityEvent *ev) +{ + fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "x=%d y=%d\n", ev->x, ev->y); +} + +static void +VerbMap(XMapEvent *ev) +{ + fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "override_redirect=%s\n", TorF(ev->override_redirect)); +} + +static void +VerbReparent(XReparentEvent *ev) +{ + fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "override_redirect=%s\n", TorF(ev->override_redirect)); +} + +static void +VerbUnmap(XUnmapEvent *ev) +{ + fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "from_configure=%s\n", TorF(ev->from_configure)); +} + +static void +VerbCirculateRequest(XCirculateRequestEvent *ev) +{ + fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "place=%s\n", Place(ev->place)); +} + +static void +VerbConfigureRequest(XConfigureRequestEvent *ev) +{ + fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); + fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); + fprintf(stderr, "border_width=%d%s", ev->border_width, sep); + fprintf(stderr, "above=%s%s", MaybeNone(ev->above), sep); + fprintf(stderr, "detail=%s%s", ConfigureDetail(ev->detail), sep); + fprintf(stderr, "value_mask=%s\n", ConfigureValueMask(ev->value_mask)); +} + +static void +VerbMapRequest(XMapRequestEvent *ev) +{ + fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); + fprintf(stderr, "window=0x%x\n", (int)ev->window); +} + +static void +VerbClient(XClientMessageEvent *ev) +{ + int i; + + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "message_type=%s%s", AtomName(ev->display, ev->message_type), sep); + fprintf(stderr, "format=%d\n", ev->format); + fprintf(stderr, "data (shown as longs)="); + for (i = 0; i < 5; i++) + fprintf(stderr, " 0x%08lx", ev->data.l[i]); + fprintf(stderr, "\n"); +} + +static void +VerbMapping(XMappingEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "request=%s%s", MappingRequest(ev->request), sep); + fprintf(stderr, "first_keycode=0x%x%s", ev->first_keycode, sep); + fprintf(stderr, "count=0x%x\n", ev->count); +} + +static void +VerbSelectionClear(XSelectionClearEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "selection=%s%s", AtomName(ev->display, ev->selection), sep); + fprintf(stderr, "time=%s\n", ServerTime(ev->time)); +} + +static void +VerbSelection(XSelectionEvent *ev) +{ + fprintf(stderr, "requestor=0x%x%s", (int)ev->requestor, sep); + fprintf(stderr, "selection=%s%s", AtomName(ev->display, ev->selection), sep); + fprintf(stderr, "target=%s%s", AtomName(ev->display, ev->target), sep); + fprintf(stderr, "property=%s%s", AtomName(ev->display, ev->property), sep); + fprintf(stderr, "time=%s\n", ServerTime(ev->time)); +} + +static void +VerbSelectionRequest(XSelectionRequestEvent *ev) +{ + fprintf(stderr, "owner=0x%x%s", (int)ev->owner, sep); + fprintf(stderr, "requestor=0x%x%s", (int)ev->requestor, sep); + fprintf(stderr, "selection=%s%s", AtomName(ev->display, ev->selection), sep); + fprintf(stderr, "target=%s%s", AtomName(ev->display, ev->target), sep); + fprintf(stderr, "property=%s%s", AtomName(ev->display, ev->property), sep); + fprintf(stderr, "time=%s\n", ServerTime(ev->time)); +} + +static void +VerbVisibility(XVisibilityEvent *ev) +{ + fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); + fprintf(stderr, "state=%s\n", VisibilityState(ev->state)); +} + +/******************************************************************************/ +/************ Return the string representation for type of an event ***********/ +/******************************************************************************/ + +char *eventtype(XEvent *ev) +{ + static char buffer[20]; + + switch (ev->type) { + case KeyPress: + return ("KeyPress"); + case KeyRelease: + return ("KeyRelease"); + case ButtonPress: + return ("ButtonPress"); + case ButtonRelease: + return ("ButtonRelease"); + case MotionNotify: + return ("MotionNotify"); + case EnterNotify: + return ("EnterNotify"); + case LeaveNotify: + return ("LeaveNotify"); + case FocusIn: + return ("FocusIn"); + case FocusOut: + return ("FocusOut"); + case KeymapNotify: + return ("KeymapNotify"); + case Expose: + return ("Expose"); + case GraphicsExpose: + return ("GraphicsExpose"); + case NoExpose: + return ("NoExpose"); + case VisibilityNotify: + return ("VisibilityNotify"); + case CreateNotify: + return ("CreateNotify"); + case DestroyNotify: + return ("DestroyNotify"); + case UnmapNotify: + return ("UnmapNotify"); + case MapNotify: + return ("MapNotify"); + case MapRequest: + return ("MapRequest"); + case ReparentNotify: + return ("ReparentNotify"); + case ConfigureNotify: + return ("ConfigureNotify"); + case ConfigureRequest: + return ("ConfigureRequest"); + case GravityNotify: + return ("GravityNotify"); + case ResizeRequest: + return ("ResizeRequest"); + case CirculateNotify: + return ("CirculateNotify"); + case CirculateRequest: + return ("CirculateRequest"); + case PropertyNotify: + return ("PropertyNotify"); + case SelectionClear: + return ("SelectionClear"); + case SelectionRequest: + return ("SelectionRequest"); + case SelectionNotify: + return ("SelectionNotify"); + case ColormapNotify: + return ("ColormapNotify"); + case ClientMessage: + return ("ClientMessage"); + case MappingNotify: + return ("MappingNotify"); + } + sprintf(buffer, "%d", ev->type); + return buffer; +} + +/******************************************************************************/ +/**************** Print the values of all fields for any event ****************/ +/******************************************************************************/ + +void printevent(XEvent *e) +{ + XAnyEvent *ev = (void*)e; + char *name; + + if(ev->window) { + XFetchName(blz.dpy, ev->window, &name); + if(name) { + fprintf(stderr, "\ttitle=%s\n", name); + XFree(name); + } + } + fprintf(stderr, "%3ld %-20s ", ev->serial, eventtype(e)); + if(ev->send_event) + fprintf(stderr, "(sendevent) "); + if(0){ + fprintf(stderr, "type=%s%s", eventtype(e), sep); + fprintf(stderr, "serial=%lu%s", ev->serial, sep); + fprintf(stderr, "send_event=%s%s", TorF(ev->send_event), sep); + fprintf(stderr, "display=0x%p%s", ev->display, sep); + } + + switch (ev->type) { + case MotionNotify: + VerbMotion((void*)ev); + break; + + case ButtonPress: + case ButtonRelease: + VerbButton((void*)ev); + break; + + case ColormapNotify: + VerbColormap((void*)ev); + break; + + case EnterNotify: + case LeaveNotify: + VerbCrossing((void*)ev); + break; + + case Expose: + VerbExpose((void*)ev); + break; + + case GraphicsExpose: + VerbGraphicsExpose((void*)ev); + break; + + case NoExpose: + VerbNoExpose((void*)ev); + break; + + case FocusIn: + case FocusOut: + VerbFocus((void*)ev); + break; + + case KeymapNotify: + VerbKeymap((void*)ev); + break; + + case KeyPress: + case KeyRelease: + VerbKey((void*)ev); + break; + + case PropertyNotify: + VerbProperty((void*)ev); + break; + + case ResizeRequest: + VerbResizeRequest((void*)ev); + break; + + case CirculateNotify: + VerbCirculate((void*)ev); + break; + + case ConfigureNotify: + VerbConfigure((void*)ev); + break; + + case CreateNotify: + VerbCreateWindow((void*)ev); + break; + + case DestroyNotify: + VerbDestroyWindow((void*)ev); + break; + + case GravityNotify: + VerbGravity((void*)ev); + break; + + case MapNotify: + VerbMap((void*)ev); + break; + + case ReparentNotify: + VerbReparent((void*)ev); + break; + + case UnmapNotify: + VerbUnmap((void*)ev); + break; + + case CirculateRequest: + VerbCirculateRequest((void*)ev); + break; + + case ConfigureRequest: + VerbConfigureRequest((void*)ev); + break; + + case MapRequest: + VerbMapRequest((void*)ev); + break; + + case ClientMessage: + VerbClient((void*)ev); + break; + + case MappingNotify: + VerbMapping((void*)ev); + break; + + case SelectionClear: + VerbSelectionClear((void*)ev); + break; + + case SelectionNotify: + VerbSelection((void*)ev); + break; + + case SelectionRequest: + VerbSelectionRequest((void*)ev); + break; + + case VisibilityNotify: + VerbVisibility((void*)ev); + break; + } +} + diff --git a/cmd/wmii/printevent.h b/cmd/wmii/printevent.h @@ -0,0 +1,2 @@ +char *eventtype(XEvent*); +void printevent(XEvent*); diff --git a/cmd/wmii/rule.c b/cmd/wmii/rule.c @@ -0,0 +1,86 @@ +/* + * Copyright ©2006 Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ + +#include <string.h> +#include <stdlib.h> +#include "wmii.h" + +/* basic rule matching language /regex/ -> value + * regex might contain POSIX regex syntax defined in regex(3) */ +enum { + IGNORE, + REGEX, + VALUE +}; + +void +trim(char *str, const char *chars) { + const char *cp; + char *sp, *sn; + + for(cp = chars; *cp; cp++) { + for(sp = sn = str; *sn; sn++) { + if(*sn != *cp) + *(sp++) = *sn; + } + *sp = 0; + } +} + +void +update_rules(Rule **rule, const char *data) { + int mode = IGNORE; + Rule *rul; + char *p, *r = nil, *v = nil, regex[256], value[256]; + + if(!data || !strlen(data)) + return; + while((rul = *rule)) { + *rule = rul->next; + regfree(&rul->regex); + free(rul); + } + for(p = (char *)data; *p; p++) + switch(mode) { + case IGNORE: + if(*p == '/') { + mode = REGEX; + r = regex; + } + else if(*p == '>') { + mode = VALUE; + value[0] = 0; + v = value; + } + break; + case REGEX: + if(*p == '/') { + mode = IGNORE; + *r = 0; + } + else { + *r = *p; + r++; + } + break; + case VALUE: + if(*p == '\n' || *p == 0) { + *rule = emallocz(sizeof(Rule)); + *v = 0; + trim(value, " \t/"); + if(!regcomp(&(*rule)->regex, regex, 0)) { + strncpy((*rule)->value, value, sizeof(rul->value)); + rule = &(*rule)->next; + } + else free(*rule); + mode = IGNORE; + } + else { + *v = *p; + v++; + } + break; + } +} diff --git a/cmd/wmii/view.c b/cmd/wmii/view.c @@ -0,0 +1,446 @@ +/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <stdlib.h> +#include <string.h> +#include "wmii.h" + +static Bool +is_empty(View *v) { + Area *a; + for(a=v->area; a; a=a->next) + if(a->frame) + return False; + return True; +} + +Frame * +clientframe_of_view(View *v, Client *c) { + Frame *f; + for(f=c->frame; f; f=f->cnext) + if(f->area->view == v) + break; + return f; +} + +static void +assign_sel_view(View *v) { + if(screen->sel != v) { + if(screen->sel) + write_event("UnfocusTag %s\n",screen->sel->name); + screen->sel = v; + write_event("FocusTag %s\n", screen->sel->name); + } +} + +Client * +sel_client_of_view(View *v) { + return v->sel && v->sel->sel ? v->sel->sel->client : nil; +} + +View * +get_view(const char *name) { + View *v; + int cmp; + + for(v = view; v; v=v->next) + if((cmp=strcmp(name, v->name)) >= 0) + break; + if(!v || cmp != 0) + v = create_view(name); + return v; +} + +View * +create_view(const char *name) { + static ushort id = 1; + View **i, *v = emallocz(sizeof(View)); + + v->id = id++; + strncpy(v->name, name, sizeof(v->name)); + create_area(v, nil, 0); + create_area(v, v->area, 0); + for(i=&view; *i; i=&(*i)->next) + if(strcmp((*i)->name, name) < 0) break; + v->next = *i; + *i = v; + write_event("CreateTag %s\n", v->name); + if(!screen->sel) + assign_sel_view(v); + return v; +} + +void +destroy_view(View *v) { + Area *a; + View **i, *tv; + + while((a = v->area)) { + v->area = a->next; + destroy_area(a); + }; + for(i=&view; *i; i=&(*i)->next) + if(*i == v) break; + *i = v->next; + write_event("DestroyTag %s\n", v->name); + if(v == screen->sel) { + for(tv=view; tv && tv->next; tv=tv->next) + if(tv->next == *i) break; + if(tv) + focus_view(screen, tv); + } + free(v); +} + +static void +update_frame_selectors(View *v) { + Area *a; + Frame *f; + + for(a=v->area; a; a=a->next) + for(f=a->frame; f; f=f->anext) + f->client->sel = f; +} + +void +focus_view(WMScreen *s, View *v) { + View *old; + Frame *f; + Client *c; + + old = screen->sel; + XGrabServer(blz.dpy); + assign_sel_view(v); + update_frame_selectors(v); + for(c=client; c; c=c->next) + if((f = c->sel)) { + if(f->view == v) { + resize_client(c, &f->rect); + update_client_grab(c); + } else { + unmap_frame(c); + unmap_client(c, IconicState); + } + } + restack_view(v); + focus_area(v->sel); + draw_frames(); + XSync(blz.dpy, False); + XUngrabServer(blz.dpy); + flushevents(EnterWindowMask, False); +} + +void +select_view(const char *arg) { + char buf[256]; + + strncpy(buf, arg, sizeof(buf)); + trim(buf, " \t+/"); + if(!strlen(buf)) + return; + if(!strncmp(buf, ".", 2) || !strncmp(buf, "..", 3)) + return; + assign_sel_view(get_view(buf)); + update_views(); /* performs focus_view */ +} + +void +attach_to_view(View *v, Frame *f) { + Client *c = f->client; + + c->revert = nil; + if(c->trans || c->floating || c->fixedsize || c->fullscreen) + v->sel = v->area; + else if(starting && v->sel->floating) + v->sel = v->area->next; + attach_to_area(v->sel, f, False); +} + +void +restack_view(View *v) { + Area *a; + Frame *f; + Client *c; + uint n, i; + static Window *wins = nil; + static uint winssz = 0; + + if(v != screen->sel) + return; + + i = 0; + n = 0; + + for(c=client; c; c=c->next) + i++; + if(i == 0) + return; + if(i >= winssz) { + winssz = 2 * i; + wins = erealloc(wins, sizeof(Window) * winssz); + } + + for(f=v->area->stack; f; f=f->snext) + if(f->client->fullscreen) + wins[n++] = f->client->framewin; + wins[n++] = screen->barwin; + for(f=v->area->stack; f; f=f->snext) + if(!f->client->fullscreen) + wins[n++] = f->client->framewin; + for(a=v->area->next; a; a=a->next) { + if(a->frame) { + wins[n++] = a->sel->client->framewin; + for(f=a->frame; f; f=f->anext) + if(f != a->sel) + wins[n++] = f->client->framewin; + } + } + if(n) + XRestackWindows(blz.dpy, wins, n); +} + +void +scale_view(View *v, float w) { + uint xoff, num_col; + uint min_width; + Area *a; + float scale, dx = 0; + int wdiff = 0; + + min_width = screen->rect.width/NCOL; + + if(!v->area->next) + return; + + num_col = 0; + for(a=v->area->next; a; a=a->next) + num_col++, dx += a->rect.width; + + scale = w / dx; + xoff = 0; + for(a=v->area->next; a; a=a->next) { + a->rect.width *= scale; + if(!a->next) + a->rect.width = w - xoff; + xoff += a->rect.width; + } + /* min_width can only be respected when there is enough space; + * the caller should guarantee this */ + if(num_col * min_width > w) + return; + xoff = 0; + for(a=v->area->next, num_col--; a; a=a->next, num_col--) { + if(a->rect.width < min_width) + a->rect.width = min_width; + else if((wdiff = xoff + a->rect.width - w + num_col * min_width) > 0) + a->rect.width -= wdiff; + if(!a->next) + a->rect.width = w - xoff; + xoff += a->rect.width; + } +} + +void +arrange_view(View *v) { + uint xoff = 0; + Area *a; + + if(!v->area->next) + return; + scale_view(v, screen->rect.width); + for(a=v->area->next; a; a=a->next) { + a->rect.x = xoff; + a->rect.y = 0; + a->rect.height = screen->rect.height - screen->brect.height; + xoff += a->rect.width; + arrange_column(a, False); + } +} + +XRectangle * +rects_of_view(View *v, uint *num, Frame *ignore) { + XRectangle *result; + Frame *f; + int i; + + i = 2; + for(f=v->area->frame; f; f=f->anext) + i++; + result = emallocz(i * sizeof(XRectangle)); + i = 0; + for(f=v->area->frame; f; f=f->anext) + if(f != ignore) + result[i++] = f->rect; + result[i++] = screen->rect; + result[i++] = screen->brect; + *num = i; + return result; +} + +/* XXX: This will need cleanup */ +uchar * +view_index(View *v) { + uint a_i, buf_i, n; + int len; + Frame *f; + Area *a; + + len = BUFFER_SIZE; + buf_i = 0; + for((a = v->area), (a_i = 0); a && len > 0; (a=a->next), (a_i++)) { + if(a->floating) + n = snprintf(&buffer[buf_i], len, "# ~ %d %d\n", + a->rect.width, a->rect.height); + else + n = snprintf(&buffer[buf_i], len, "# %d %d %d\n", + a_i, a->rect.x, a->rect.width); + buf_i += n; + len -= n; + for(f=a->frame; f && len > 0; f=f->anext) { + XRectangle *r = &f->rect; + if(a->floating) + n = snprintf(&buffer[buf_i], len, "~ 0x%x %d %d %d %d %s\n", + (uint)f->client->win, + r->x, r->y, r->width, r->height, + f->client->props); + else + n = snprintf(&buffer[buf_i], len, "%d 0x%x %d %d %s\n", + a_i, (uint)f->client->win, r->y, + r->height, f->client->props); + if(len - n < 0) + return (uchar*)buffer; + buf_i += n; + len -= n; + } + } + return (uchar *)buffer; +} + +Client * +client_of_message(View *v, char *message, uint *next) { + ulong id = 0; + Client *c; + + if(!strncmp(message, "sel ", 4)) { + *next = 4; + return sel_client_of_view(v); + } + sscanf(message, "0x%lx %n", &id, next); + if(!id) + sscanf(message, "%lu %n", &id, next); + if(!id) + return nil; + for(c=client; c && c->win!=id; c=c->next); + return c; +} + +Area * +area_of_message(View *v, char *message, uint *next) { + uint i; + Area *a; + + if(!strncmp(message, "sel ", 4)) { + *next = 4; + return v->sel; + } + if(!strncmp(message, "~ ", 2)) { + *next = 2; + return v->area; + } + if(1 != sscanf(message, "%u %n", &i, next) || i == 0) + return nil; + for(a=v->area; i && a; a=a->next) + i--; + return a; +} + +char * +message_view(View *v, char *message) { + uint n, i; + Client *c; + Frame *f; + Area *a; + Bool swap; + static char Ebadvalue[] = "bad value"; + + if(!strncmp(message, "send ", 5)) { + message += 5; + swap = False; + goto send; + } + if(!strncmp(message, "swap ", 5)) { + message += 5; + swap = True; + goto send; + } + if(!strncmp(message, "select ", 7)) { + message += 7; + return select_area(v->sel, message); + } + if(!strncmp(message, "colmode ", 8)) { + message += 8; + if(!(a = area_of_message(v, message, &n)) || a == v->area) + return Ebadvalue; + if((i = column_mode_of_str(&message[n])) == -1) + return Ebadvalue; + a->mode = i; + arrange_column(a, True); + restack_view(v); + if(v == screen->sel) + focus_view(screen, v); + draw_frames(); + return nil; + } + return Ebadvalue; + +send: + if(!(c = client_of_message(v, message, &n))) + return Ebadvalue; + if(!(f = clientframe_of_view(v, c))) + return Ebadvalue; + return send_client(f, &message[n], swap); +} + +void +update_views() { + View *n, *v; + View *old = screen->sel; + + for(v=view; v; v=v->next) + update_frame_selectors(v); + if(old && !strncmp(old->name, "nil", 4)) + old = nil; + for((v=view) && (n=v->next); v; (v=n) && (n=v->next)) + if((v != old) && is_empty(v)) + destroy_view(v); + if(old) + focus_view(screen, old); + else if(screen->sel) + focus_view(screen, screen->sel); +} + +uint +newcolw_of_view(View *v) { + Rule *r; + Area *a; + uint i, n; + regmatch_t tmpregm; + + for(r=def.colrules.rule; r; r=r->next) { + if(!regexec(&r->regex, v->name, 1, &tmpregm, 0)) { + char buf[256]; + char *toks[16]; + strncpy(buf, r->value, sizeof(buf)); + n = tokenize(toks, 16, buf, '+'); + for(a=v->area, i=0; a; a=a->next) + i++; + if(n && n >= i) { + if(sscanf(toks[i - 1], "%u", &n) == 1) + return (screen->rect.width * n) / 100; + } + break; + } + } + return 0; +} diff --git a/cmd/wmii/wmii.h b/cmd/wmii/wmii.h @@ -0,0 +1,439 @@ +/* + * © 2004-2006 Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + */ + +#include <stdio.h> +#include <regex.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include <ixp.h> +#include <util.h> + +#define nil ((void*)0) +#define nelem(ary) (sizeof(ary) / sizeof(*ary)) + +/* Types */ +#undef uchar +#undef ushort +#undef uint +#undef ulong +#undef uvlong +#undef vlong +#define uchar _wmiiuchar +#define ushort _wmiiushort +#define uint _wmiiuint +#define ulong _wmiiulong +#define vlong _wmiivlong +#define uvlong _wmiiuvlong +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned long long uvlong; +typedef long long vlong; + +#define BLITZ_FONT "-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*-*" +#define BLITZ_FOCUSCOLORS "#ffffff #335577 #447799" +#define BLITZ_NORMCOLORS "#222222 #eeeeee #666666" + +typedef struct Blitz Blitz; +typedef struct BlitzColor BlitzColor; +typedef struct BlitzFont BlitzFont; +typedef struct BlitzBrush BlitzBrush; + +struct Blitz { + Display *dpy; + int screen; + Window root; +}; + +enum BlitzAlign { + NORTH = 0x01, + EAST = 0x02, + SOUTH = 0x04, + WEST = 0x08, + NEAST = NORTH | EAST, + NWEST = NORTH | WEST, + SEAST = SOUTH | EAST, + SWEST = SOUTH | WEST, + CENTER = NEAST | SWEST +}; + +typedef enum BlitzAlign BlitzAlign; + +struct BlitzColor { + vlong bg; + vlong fg; + vlong border; + char colstr[24]; /* #RRGGBB #RRGGBB #RRGGBB */ +}; + +struct BlitzFont { + XFontStruct *xfont; + XFontSet set; + int ascent; + int descent; + uint height; + char *fontstr; +}; + +struct BlitzBrush { + Blitz *blitz; + Drawable drawable; + GC gc; + int border; + BlitzColor color; + BlitzAlign align; + BlitzFont *font; + XRectangle rect; /* relative rect */ +}; + +enum { +/* WM atom */ + WMState, WMProtocols, WMDelete, +/* NET atom */ + NetSupported, NetWMName, +/* Other */ + TagsAtom, +/* Last atom */ + AtomLast +}; + +/* Column modes */ +enum { Coldefault, Colstack, Colmax }; + +/* Cursor */ +enum { CurNormal, CurNECorner, CurNWCorner, CurSECorner, CurSWCorner, + CurMove, CurInput, CurInvisible, CurLast }; + +enum { NCOL = 16 }; +enum { WM_PROTOCOL_DELWIN = 1 }; + +/* Data Structures */ +typedef struct View View; +typedef struct Area Area; +typedef struct Frame Frame; +typedef struct Client Client; +typedef struct Key Key; +typedef struct Bar Bar; +typedef struct Rule Rule; +typedef struct Ruleset Ruleset; +typedef struct WMScreen WMScreen; + +struct View { + View *next; + char name[256]; + ushort id; + Area *area; + Area *sel; + Area *revert; +}; + +struct Area { + Area *next; + Frame *frame; + Frame *stack; + Frame *sel; + View *view; + Bool floating; + ushort id; + int mode; + XRectangle rect; +}; + +struct Frame { + Frame *cnext; + Frame *anext; + Frame *snext; + View *view; + Area *area; + ushort id; + XRectangle rect; + XRectangle crect; + XRectangle revert; + Client *client; + Bool collapsed; + XRectangle grabbox; + XRectangle titlebar; +}; + +struct Client { + Client *next; + Area *revert; + Frame *frame; + Frame *sel; + char name[256]; + char tags[256]; + char props[512]; + uint border; + int proto; + Bool floating; + Bool fixedsize; + Bool fullscreen; + Bool urgent; + Bool mapped; + Bool frame_mapped; + int unmapped; + Window win; + Window trans; + Window framewin; + Cursor cursor; + XRectangle rect; + XSizeHints size; + GC gc; +}; + +struct Key { + Key *next; + Key *lnext; + Key *tnext; + ushort id; + char name[128]; + ulong mod; + KeyCode key; +}; + +struct Bar { + Bar *next; + Bar *smaller; + char buf[280]; + char text[256]; + char name[256]; + ushort id; + BlitzBrush brush; +}; + +struct Rule { + Rule *next; + regex_t regex; + char value[256]; +}; + +struct Ruleset { + Rule *rule; + char *string; + uint size; +}; + +/* global variables */ +struct { + BlitzColor focuscolor; + BlitzColor normcolor; + BlitzFont font; + uint border; + uint snap; + char *keys; + uint keyssz; + Ruleset tagrules; + Ruleset colrules; + char grabmod[5]; + ulong mod; + int colmode; +} def; + +enum { BarLeft, BarRight }; + +struct WMScreen { + Bar *bar[2]; + View *sel; + Client *focus; + Client *hasgrab; + Window barwin; + + XRectangle rect; + XRectangle brect; + BlitzBrush bbrush; +} *screens, *screen; + +Client *client; +View *view; +Key *key; +Client c_magic; +Client c_root; + +enum { BUFFER_SIZE = 8092 }; +char buffer[BUFFER_SIZE]; + +/* IXP */ +IxpServer srv; +Ixp9Srv p9srv; + +/* X11 */ +uint num_screens; +Blitz blz; +GC xorgc; +char *user; +Atom atom[AtomLast]; +Cursor cursor[CurLast]; +uint valid_mask; +uint num_lock_mask; +Bool sel_screen; +Pixmap pmap; +void (*handler[LASTEvent]) (XEvent *); + +/* Misc */ +Bool starting; +Bool verbose; +char *execstr; + +/* wm.c */ +char *message_root(char *message); + +/* area.c */ +Area *create_area(View *v, Area *pos, uint w); +void destroy_area(Area *a); +Area *area_of_id(View *t, ushort id); +void focus_area(Area *a); +char *select_area(Area *a, char *arg); +void send_to_area(Area *to, Frame *f); +void attach_to_area(Area *a, Frame *f, Bool send); +void detach_from_area(Frame *f); +Client *sel_client_of_area(Area *a); + +/* bar.c */ +Bar *create_bar(Bar **b_link, char *name); +void destroy_bar(Bar **b_link, Bar *b); +void draw_bar(WMScreen *s); +void draw_border(BlitzBrush *b); +void resize_bar(); +Bar *bar_of_name(Bar *b_link, const char *name); + +/* client.c */ +Client *create_client(Window w, XWindowAttributes *wa); +void destroy_client(Client *c); +void configure_client(Client *c); +void prop_client(Client *c, Atom a); +void kill_client(Client *c); +void gravitate_client(Client *c, Bool invert); +void map_client(Client *c); +void unmap_client(Client *c, int state); +void map_frame(Client *c); +void unmap_frame(Client *c); +void set_cursor(Client *c, Cursor cur); +void focus_frame(Frame *f, Bool restack); +void reparent_client(Client *c, Window w, int x, int y); +void manage_client(Client *c); +void focus(Client *c, Bool restack); +void focus_client(Client *c); +void resize_client(Client *c, XRectangle *r); +void apply_sizehints(Client *c, XRectangle *r, Bool floating, Bool frame, BlitzAlign sticky); +char *send_client(Frame *f, char *arg, Bool swap); +char * message_client(Client *c, char *message); +void move_client(Client *c, char *arg); +void size_client(Client *c, char *arg); +Client *sel_client(); +Frame *frame_of_win(Window w); +Client *client_of_win(Window w); +void update_client_grab(Client *c); +void apply_rules(Client *c); +void apply_tags(Client *c, const char *tags); + +/* column.c */ +void arrange_column(Area *a, Bool dirty); +void resize_column(Client *c, XRectangle *r); +int column_mode_of_str(char *arg); +char *str_of_column_mode(int mode); +Area *new_column(View *v, Area *pos, uint w); + +/* draw.c */ +int loadcolor(Blitz *blitz, BlitzColor *c); +void draw_label(BlitzBrush *b, char *text); +void draw_tile(BlitzBrush *b); +void draw_rect(BlitzBrush *b); + +void drawbg(Display *dpy, Drawable drawable, GC gc, + XRectangle *rect, BlitzColor c, Bool fill, Bool border); +void drawcursor(Display *dpy, Drawable drawable, GC gc, + int x, int y, uint h, BlitzColor c); +uint textwidth(BlitzFont *font, char *text); +uint textwidth_l(BlitzFont *font, char *text, uint len); +void loadfont(Blitz *blitz, BlitzFont *font); +uint labelh(BlitzFont *font); +char *parse_colors(char **buf, int *buflen, BlitzColor *col); + +/* event.c */ +void dispatch_event(XEvent *e); +void check_x_event(IxpConn *c); +uint flushevents(long even_mask, Bool dispatch); + +/* frame.c */ +Frame *create_frame(Client *c, View *v); +void remove_frame(Frame *f); +void insert_frame(Frame *pos, Frame *f, Bool before); +void resize_frame(Frame *f, XRectangle *r); +Bool frame_to_top(Frame *f); +void set_frame_cursor(Frame *f, int x, int y); +void swap_frames(Frame *fa, Frame *fb); +int frame_delta_h(); +void frame2client(XRectangle *r); +void client2frame(XRectangle *r); +void draw_frame(Frame *f); +void draw_frames(); +void update_frame_widget_colors(Frame *f); +void check_frame_constraints(XRectangle *rect); + +/* fs.c */ +void fs_attach(Ixp9Req *r); +void fs_clunk(Ixp9Req *r); +void fs_create(Ixp9Req *r); +void fs_flush(Ixp9Req *r); +void fs_freefid(Fid *f); +void fs_open(Ixp9Req *r); +void fs_read(Ixp9Req *r); +void fs_remove(Ixp9Req *r); +void fs_stat(Ixp9Req *r); +void fs_walk(Ixp9Req *r); +void fs_write(Ixp9Req *r); +void write_event(char *format, ...); + +/* geom.c */ +Bool ptinrect(int x, int y, XRectangle * r); +BlitzAlign quadrant(XRectangle *rect, int x, int y); +Cursor cursor_of_quad(BlitzAlign align); +int strtorect(XRectangle *r, const char *val); +BlitzAlign get_sticky(XRectangle *src, XRectangle *dst); +int r_east(XRectangle *r); +int r_south(XRectangle *r); + +/* key.c */ +void kpress(Window w, ulong mod, KeyCode keycode); +void update_keys(); +void init_lock_keys(); +ulong mod_key_of_str(char *val); + +/* mouse.c */ +void do_mouse_resize(Client *c, Bool grabbox, BlitzAlign align); +void grab_mouse(Window w, ulong mod, ulong button); +void ungrab_mouse(Window w, ulong mod, uint button); +BlitzAlign snap_rect(XRectangle *rects, int num, XRectangle *current, + BlitzAlign *mask, int snap); +void grab_button(Window w, uint button, ulong mod); + +/* rule.c */ +void update_rules(Rule **rule, const char *data); +void trim(char *str, const char *chars); + +/* view.c */ +void arrange_view(View *v); +void scale_view(View *v, float w); +View *get_view(const char *name); +View *create_view(const char *name); +void focus_view(WMScreen *s, View *v); +void update_client_views(Client *c, char **tags); +XRectangle *rects_of_view(View *v, uint *num, Frame *ignore); +View *view_of_id(ushort id); +void select_view(const char *arg); +void attach_to_view(View *v, Frame *f); +Client *sel_client_of_view(View *v); +char *message_view(View *v, char *message); +void restack_view(View *v); +uchar *view_index(View *v); +void destroy_view(View *v); +void update_views(); +uint newcolw_of_view(View *v); + +/* wm.c */ +int wmii_error_handler(Display *dpy, XErrorEvent *error); +int win_proto(Window w); diff --git a/cmd/wmii9menu.c b/cmd/wmii9menu.c @@ -0,0 +1,527 @@ +/* Licence + * ======= + * + * 9menu is free software, and is Copyright (c) 1994 by David Hogan and + * Arnold Robbins. Permission is granted to all sentient beings to use + * this software, to make copies of it, and to distribute those copies, + * provided that: + * + * (1) the copyright and licence notices are left intact + * (2) the recipients are aware that it is free software + * (3) any unapproved changes in functionality are either + * (i) only distributed as patches + * or (ii) distributed as a new program which is not called 9menu + * and whose documentation gives credit where it is due + * (4) the authors are not held responsible for any defects + * or shortcomings in the software, or damages caused by it. + * + * There is no warranty for this software. Have a nice day. + * + * -- + * Arnold Robbins + * arnold@skeeve.com + * + * 9menu.c + * + * This program puts up a window that is just a menu, and executes + * commands that correspond to the items selected. + * + * Initial idea: Arnold Robbins + * Version using libXg: Matty Farrow (some ideas borrowed) + * This code by: David Hogan and Arnold Robbins + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <X11/Xutil.h> + +#define nil ((void*)0) + +char version[] = "@(#) wmii9menu version 1.8"; + +/* lovely X stuff */ +Display *dpy; +int screen; +Window root; +Window menuwin; +Colormap defcmap; +XColor color; +XFontStruct *font; +GC gc; + +unsigned long selbg; +unsigned long selfg; +unsigned long normbg; +unsigned long normfg; +unsigned long border; +char *sfgname = nil; +char *sbgname = nil; +char *nfgname = nil; +char *nbgname = nil; +char *brcname = nil; + +/* for XSetWMProperties to use */ +int g_argc; +char **g_argv; + +/* for labels read from files */ +int f_argc; +char **f_argv; + +char *initial = ""; +int cur; + +char *fontlist[] = { /* default font list if no -font */ + "pelm.latin1.9", + "lucm.latin1.9", + "blit", + "9x15bold", + "9x15", + "lucidasanstypewriter-12", + "fixed", + nil +}; + +char *progname; /* my name */ +char *displayname; /* X display */ +char *fontname; /* font */ +char *filename; /* file to read options or labels from */ + +char **labels; /* list of labels and commands */ +char **commands; +int numitems; + +void usage(), run_menu(); +void create_window(); +void redraw(), warpmouse(); +void memory(); +int args(); + +/* memory --- print the out of memory message and die */ + +void +memory(s) +char *s; +{ + fprintf(stderr, "%s: couldn't allocate memory for %s\n", progname, s); + exit(1); +} + +/* args --- go through the argument list, set options */ + +int +args(int argc, char **argv) +{ + int i; + if (argc == 0 || argv == nil || argv[0] == '\0') + return -1; + for (i = 0; i < argc && argv[i] != nil; i++) { + if (strcmp(argv[i], "-display") == 0) + displayname = argv[++i]; + else if (strcmp(argv[i], "-file") == 0) + filename = argv[++i]; + else if (strcmp(argv[i], "-font") == 0) + fontname = argv[++i]; + else if (strncmp(argv[i], "-initial", 9) == 0) + initial = argv[++i]; + else if (strncmp(argv[i], "-nb", 3) == 0) + nbgname = argv[i][3] ? &argv[i][3] : argv[++i]; + else if (strncmp(argv[i], "-nf", 3) == 0) + nfgname = argv[i][3] ? &argv[i][3] : argv[++i]; + else if (strncmp(argv[i], "-sb", 3) == 0) + sbgname = argv[i][3] ? &argv[i][3] : argv[++i]; + else if (strncmp(argv[i], "-sf", 3) == 0) + sfgname = argv[i][3] ? &argv[i][3] : argv[++i]; + else if (strncmp(argv[i], "-br", 3) == 0) + brcname = argv[i][3] ? &argv[i][3] : argv[++i]; + else if (strcmp(argv[i], "-version") == 0) { + printf("%s\n", version); + exit(0); + } else if (argv[i][0] == '-') + usage(); + else + break; + } + return i; +} + +/* main --- crack arguments, set up X stuff, run the main menu loop */ + +int +main(int argc, char **argv) +{ + int i, j; + char *cp; + XGCValues gv; + unsigned long mask; + int nlabels = 0; + + g_argc = argc; + g_argv = argv; + + /* set default label name */ + if ((cp = strrchr(argv[0], '/')) == nil) + progname = argv[0]; + else + progname = ++cp; + + ++argv; + --argc; + + i = args(argc, argv); + + numitems = argc - i; + + if (numitems <= 0 && filename == nil) + usage(); + + if (filename) { + /* Read options and labels from file */ + char fbuf[1024]; + FILE *fp; + + fp = fopen(filename, "r"); + if (fp == nil) { + fprintf(stderr, "%s: couldn't open '%s'\n", progname, + filename); + exit(1); + } + while (fgets(fbuf, sizeof fbuf, fp)) { + char *s = fbuf; + strtok(s, "\n"); + if (s[0] == '-') { + char *temp[3]; + temp[0] = s; + temp[1] = strchr(s, ' '); + if (temp[1]) { + *(temp[1]++) = '\0'; + s = malloc(strlen(temp[1]) + 1); + if (s == nil) + memory("temporary argument"); + strcpy(s, temp[1]); + temp[1] = s; + } + temp[2] = 0; + args(temp[1] ? 2 : 1, temp); + continue; + } + if (s[0] == '#') + continue; + /* allow - in menu items to be escaped */ + if (s[0] == '\\') + ++s; + /* allocate space */ + if (f_argc < nlabels + 1) { + int k; + char **temp = malloc(sizeof(char *) * (f_argc + 5)); + if (temp == 0) + memory("temporary item"); + + for (k = 0; k < nlabels; k++) + temp[k] = f_argv[k]; + + free(f_argv); + f_argv = temp; + f_argc += 5; + } + f_argv[nlabels] = malloc(strlen(s) + 1); + if (f_argv[nlabels] == nil) + memory("temporary text"); + strcpy(f_argv[nlabels], s); + ++nlabels; + } + } + + labels = (char **) malloc((numitems + nlabels) * sizeof(char *)); + commands = (char **) malloc((numitems + nlabels) * sizeof(char *)); + if (commands == nil || labels == nil) + memory("command and label arrays"); + + for (j = 0; j < numitems; j++) { + labels[j] = argv[i + j]; + if ((cp = strchr(labels[j], ':')) != nil) { + *cp++ = '\0'; + commands[j] = cp; + } else + commands[j] = labels[j]; + if(strcmp(labels[j], initial) == 0) + cur = j; + } + + /* + * Now we no longer need i (our offset into argv) so we recycle it, + * while keeping the old value of j! + */ + for (i = 0; i < nlabels; i++) { + labels[j] = f_argv[i]; + if ((cp = strchr(labels[j], ':')) != nil) { + *cp++ = '\0'; + commands[j] = cp; + } else + commands[j] = labels[j]; + ++j; + } + + /* And now we merge the totals */ + numitems += nlabels; + + dpy = XOpenDisplay(displayname); + if (dpy == nil) { + fprintf(stderr, "%s: cannot open display", progname); + if (displayname != nil) + fprintf(stderr, " %s", displayname); + fprintf(stderr, "\n"); + exit(1); + } + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + /* + * This used to be + * black = BlackPixel(dpy, screen); + * white = WhitePixel(dpy, screen); + */ + defcmap = DefaultColormap(dpy, screen); + if (sbgname == nil + || XParseColor(dpy, defcmap, sbgname, &color) == 0 + || XAllocColor(dpy, defcmap, &color) == 0) + selbg = BlackPixel(dpy, screen); + else + selbg = color.pixel; + + if (sfgname == nil + || XParseColor(dpy, defcmap, sfgname, &color) == 0 + || XAllocColor(dpy, defcmap, &color) == 0) + selfg = WhitePixel(dpy, screen); + else + selfg = color.pixel; + + if (nbgname == nil + || XParseColor(dpy, defcmap, nbgname, &color) == 0 + || XAllocColor(dpy, defcmap, &color) == 0) + normbg = selfg; + else + normbg = color.pixel; + + if (nfgname == nil + || XParseColor(dpy, defcmap, nfgname, &color) == 0 + || XAllocColor(dpy, defcmap, &color) == 0) + normfg = selbg; + else + normfg = color.pixel; + + if (brcname == nil + || XParseColor(dpy, defcmap, brcname, &color) == 0 + || XAllocColor(dpy, defcmap, &color) == 0) + border = selbg; + else + border = color.pixel; + + /* try user's font first */ + if (fontname != nil) { + font = XLoadQueryFont(dpy, fontname); + if (font == nil) + fprintf(stderr, "%s: warning: can't load font %s\n", + progname, fontname); + } + + /* if no user font, try one of our default fonts */ + if (font == nil) { + for (i = 0; fontlist[i] != nil; i++) { + font = XLoadQueryFont(dpy, fontlist[i]); + if (font != nil) + break; + } + } + + if (font == nil) { + fprintf(stderr, "%s: fatal: cannot load a font\n", progname); + exit(1); + } + + gv.foreground = normfg; + gv.background = normbg; + gv.font = font->fid; + gv.line_width = 0; + mask = GCForeground | GCBackground | GCFont | GCLineWidth; + gc = XCreateGC(dpy, root, mask, &gv); + + run_menu(); + + XCloseDisplay(dpy); + exit(0); +} + +/* usage --- print a usage message and die */ + +void +usage() +{ + fprintf(stderr, "usage: %s [-display <displayname>] [-font <fontname>] ", progname); + fprintf(stderr, "[-file filename] "); + fprintf(stderr, "[-{n,s}{f,b} <color>] [-br <color>] "); + fprintf(stderr, "[-version] menitem[:command] ...\n"); + exit(0); +} + +/* run_menu --- put up the window, execute selected commands */ + +void +run_menu() +{ + XEvent ev; + int i, old, wide, high, dx, dy; + + dx = 0; + for (i = 0; i < numitems; i++) { + wide = XTextWidth(font, labels[i], strlen(labels[i])) + 4; + if (wide > dx) + dx = wide; + } + wide = dx; + + high = font->ascent + font->descent + 1; + dy = numitems * high; + + enum { + MouseMask = + ButtonPressMask + | ButtonReleaseMask + | ButtonMotionMask + | PointerMotionMask, + MenuMask = + MouseMask + | StructureNotifyMask + | ExposureMask + }; + + create_window(wide, high); + warpmouse(wide, high); + + XSelectInput(dpy, menuwin, MenuMask); + + XMapWindow(dpy, menuwin); + + i = 0; /* save menu Item position */ + + for (;;) { + XNextEvent(dpy, &ev); + switch (ev.type) { + default: + fprintf(stderr, "%s: unknown ev.type %d\n", + progname, ev.type); + break; + case ButtonRelease: + i = ev.xbutton.y/high; + if (ev.xbutton.x < 0 || ev.xbutton.x > wide) + return; + else if (i < 0 || i >= numitems) + return; + + printf("%s\n", commands[i]); + return; + case ButtonPress: + case MotionNotify: + old = cur; + cur = ev.xbutton.y/high; + if (ev.xbutton.x < 0 || ev.xbutton.x > wide) + cur = ~0; + if (cur == old) + break; + redraw(high, wide); + break; + case MapNotify: + redraw(high, wide); + if(XGrabPointer(dpy, menuwin, False, MouseMask, + GrabModeAsync, GrabModeAsync, + 0, None, CurrentTime + ) != GrabSuccess) { + fprintf(stderr, "Failed to grab the mouse\n"); + } + break; + case Expose: + redraw(high, wide); + break; + case MappingNotify: /* why do we get this? */ + break; + } + } +} + +/* set_wm_hints --- set all the window manager hints */ + +void +create_window(int wide, int high) +{ + XSetWindowAttributes wa = { 0 }; + unsigned int h; + int x, y, dummy; + Window wdummy; + + h = high * numitems; + + XQueryPointer(dpy, root, &wdummy, &wdummy, &x, &y, + &dummy, &dummy, (uint*)&dummy); + x -= wide / 2; + if (x < 0) + x = 0; + else if (x + wide > DisplayWidth(dpy, screen)) + x = DisplayWidth(dpy, screen) - wide; + + y -= cur * high + high / 2; + if (y < 0) + y = 0; + else if (y + h > DisplayHeight(dpy, screen)) + y = DisplayHeight(dpy, screen) - h; + + wa.override_redirect = True; + wa.border_pixel = border; + wa.background_pixel = normbg; + menuwin = XCreateWindow(dpy, root, x, y, wide, h, + 1, DefaultDepth(dpy, screen), CopyFromParent, + DefaultVisual(dpy, screen), + CWOverrideRedirect + | CWBackPixel + | CWBorderPixel + | CWEventMask, + &wa); + + XSetCommand(dpy, menuwin, g_argv, g_argc); +} + +/* redraw --- actually redraw the menu */ + +void +redraw(int high, int wide) +{ + int tx, ty, i; + + for (i = 0; i < numitems; i++) { + tx = (wide - XTextWidth(font, labels[i], strlen(labels[i]))) / 2; + ty = i*high + font->ascent + 1; + if (cur == i) + XSetForeground(dpy, gc, selbg); + else + XSetForeground(dpy, gc, normbg); + XFillRectangle(dpy, menuwin, gc, 0, i*high, wide, high); + if (cur == i) + XSetForeground(dpy, gc, selfg); + else + XSetForeground(dpy, gc, normfg); + XDrawString(dpy, menuwin, gc, tx, ty, labels[i], strlen(labels[i])); + } +} + +/* warpmouse --- bring the mouse to the menu */ + +void +warpmouse(int wide, int high) +{ + int offset; + + /* move tip of pointer into middle of menu item */ + offset = (font->ascent + font->descent + 1) / 2; + offset += 6; /* fudge factor */ + + XWarpPointer(dpy, None, menuwin, 0, 0, 0, 0, + wide/2, cur*high-high/2+offset); +} diff --git a/cmd/wmii9rc.sh b/cmd/wmii9rc.sh @@ -0,0 +1,19 @@ +#!/bin/sh -f +RC="" +for i in "$PLAN9" P9PATHS; do + if [ -d "$i" -a -x "$i/bin/rc" ]; then + export PLAN9="$i" + RC="$i/bin/rc" + break; + fi +done + +if [ ! -n "$RC" ]; then + exit 1 +fi + +if [ -n "$1" ]; then + exec $RC $@ +else + true +fi diff --git a/cmd/wmiiloop.awk b/cmd/wmiiloop.awk @@ -0,0 +1,35 @@ +#!AWKPATH -f +BEGIN { + arg[1] = "Nop" + body = ""; + writekeys = "wmiir write /keys" + print "IFS=''" +} + +function addevent() { + if(arg[1] == "Key") + keys[arg[2]] = 1; + + var = arg[1] "s" + print var "=\"$" var " " arg[2] "\"" + + gsub("[^a-zA-Z_0-9]", "_", arg[2]); + if(body != "") + print arg[1] "_" arg[2] "() {" body "\n}" +} + +/^(Event|Key|Action)[ \t]/ { + addevent() + split($0, arg) + body = "" +} +/^[ \t]/ { + body = body"\n"$0 +} + +END { + addevent() + for(key in keys) + print key | writekeys; + close(writekeys); +} diff --git a/cmd/wmiir.c b/cmd/wmiir.c @@ -0,0 +1,321 @@ +/* Copyright ©2007 Kris Maglione <fbsdaemon@gmail.com> + * See LICENSE file for license details. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <ixp.h> +#include <util.h> + +static IxpClient *client; + +static void +usage() { + fprintf(stderr, + "usage: %1$s [-a <address>] {create | read | ls [-ld] | remove | write} <file>\n" + " %1$s [-a <address>] xwrite <file> <data>\n" + " %1$s -v\n", argv0); + exit(1); +} + +/* Utility Functions */ +static void +write_data(IxpCFid *fid) { + void *buf; + uint len; + + buf = ixp_emalloc(fid->iounit);; + while((len = read(0, buf, fid->iounit)) > 0) + if(ixp_write(fid, buf, len) != len) + fatal("cannot write file: %s\n", errstr); + /* do an explicit empty write when no writing has been done yet */ + if(fid->offset == 0) + if(ixp_write(fid, buf, 0) != 0) + fatal("cannot write file: %s\n", errstr); + free(buf); +} + +static int +comp_stat(const void *s1, const void *s2) { + Stat *st1, *st2; + + st1 = (Stat*)s1; + st2 = (Stat*)s2; + return strcmp(st1->name, st2->name); +} + +static void +setrwx(long m, char *s) { + static char *modes[] = { + "---", "--x", "-w-", + "-wx", "r--", "r-x", + "rw-", "rwx", + }; + strncpy(s, modes[m], 3); +} + +static char * +str_of_mode(uint mode) { + static char buf[16]; + + buf[0]='-'; + if(mode & P9_DMDIR) + buf[0]='d'; + buf[1]='-'; + setrwx((mode >> 6) & 7, &buf[2]); + setrwx((mode >> 3) & 7, &buf[5]); + setrwx((mode >> 0) & 7, &buf[8]); + buf[11] = 0; + return buf; +} + +static char * +str_of_time(uint val) { + static char buf[32]; + + ctime_r((time_t*)&val, buf); + buf[strlen(buf) - 1] = '\0'; + return buf; +} + +static void +print_stat(Stat *s, int details) { + if(details) + fprintf(stdout, "%s %s %s %5llu %s %s\n", str_of_mode(s->mode), + s->uid, s->gid, s->length, str_of_time(s->mtime), s->name); + else { + if((s->mode&P9_DMDIR) && strcmp(s->name, "/")) + fprintf(stdout, "%s/\n", s->name); + else + fprintf(stdout, "%s\n", s->name); + } +} + +/* Service Functions */ +static int +xwrite(int argc, char *argv[]) { + IxpCFid *fid; + char *file; + + ARGBEGIN{ + default: + usage(); + }ARGEND; + + file = EARGF(usage()); + fid = ixp_open(client, file, P9_OWRITE); + if(fid == nil) + fatal("Can't open file '%s': %s\n", file, errstr); + + write_data(fid); + return 0; +} + +static int +xawrite(int argc, char *argv[]) { + IxpCFid *fid; + char *file, *buf, *arg; + int nbuf, mbuf, len; + + ARGBEGIN{ + default: + usage(); + }ARGEND; + + file = EARGF(usage()); + fid = ixp_open(client, file, P9_OWRITE); + if(fid == nil) + fatal("Can't open file '%s': %s\n", file, errstr); + + nbuf = 0; + mbuf = 128; + buf = ixp_emalloc(mbuf); + while(argc) { + arg = ARGF(); + len = strlen(arg); + if(nbuf + len > mbuf) { + mbuf <<= 1; + buf = ixp_erealloc(buf, mbuf); + } + memcpy(buf+nbuf, arg, len); + nbuf += len; + if(argc) + buf[nbuf++] = ' '; + } + + if(ixp_write(fid, buf, nbuf) == -1) + fatal("cannot write file '%s': %s\n", file, errstr); + return 0; +} + +static int +xcreate(int argc, char *argv[]) { + IxpCFid *fid; + char *file; + + ARGBEGIN{ + default: + usage(); + }ARGEND; + + file = EARGF(usage()); + fid = ixp_create(client, file, 0777, P9_OREAD); + if(fid == nil) + fatal("Can't create file '%s': %s\n", file, errstr); + + if((fid->qid.type&P9_DMDIR) == 0) + write_data(fid); + + return 0; +} + +static int +xremove(int argc, char *argv[]) { + char *file; + + ARGBEGIN{ + default: + usage(); + }ARGEND; + + file = EARGF(usage()); + if(ixp_remove(client, file) == 0) + fatal("Can't remove file '%s': %s\n", file, errstr); + return 0; +} + +static int +xread(int argc, char *argv[]) { + IxpCFid *fid; + char *file, *buf; + int count; + + ARGBEGIN{ + default: + usage(); + }ARGEND; + + file = EARGF(usage()); + fid = ixp_open(client, file, P9_OREAD); + if(fid == nil) + fatal("Can't open file '%s': %s\n", file, errstr); + + buf = ixp_emalloc(fid->iounit); + while((count = ixp_read(fid, buf, fid->iounit)) > 0) + write(1, buf, count); + + if(count == -1) + fatal("cannot read file/directory '%s': %s\n", file, errstr); + + return 0; +} + +static int +xls(int argc, char *argv[]) { + Message m; + Stat *stat; + IxpCFid *fid; + char *file, *buf; + int lflag, dflag, count, nstat, mstat, i; + + lflag = dflag = 0; + + ARGBEGIN{ + case 'l': + lflag++; + break; + case 'd': + dflag++; + break; + default: + usage(); + }ARGEND; + + file = EARGF(usage()); + + stat = ixp_stat(client, file); + if(stat == nil) + fatal("cannot stat file '%s': %s\n", file, errstr); + + if(dflag || (stat->mode&P9_DMDIR) == 0) { + print_stat(stat, lflag); + ixp_freestat(stat); + return 0; + } + ixp_freestat(stat); + + fid = ixp_open(client, file, P9_OREAD); + if(fid == nil) + fatal("Can't open file '%s': %s\n", file, errstr); + + nstat = 0; + mstat = 16; + stat = ixp_emalloc(sizeof(*stat) * mstat); + buf = ixp_emalloc(fid->iounit); + while((count = ixp_read(fid, buf, fid->iounit)) > 0) { + m = ixp_message(buf, count, MsgUnpack); + while(m.pos < m.end) { + if(nstat == mstat) { + mstat <<= 1; + stat = ixp_erealloc(stat, mstat); + } + ixp_pstat(&m, &stat[nstat++]); + } + } + + qsort(stat, nstat, sizeof(*stat), comp_stat); + for(i = 0; i < nstat; i++) { + print_stat(&stat[i], lflag); + ixp_freestat(&stat[i]); + } + free(stat); + + if(count == -1) + fatal("cannot read directory '%s': %s\n", file, errstr); + return 0; +} + +int +main(int argc, char *argv[]) { + int ret; + char *cmd, *address; + + address = getenv("WMII_ADDRESS"); + + ARGBEGIN{ + case 'v': + puts("ixpc-" VERSION ", ©2007 Kris Maglione\n"); + exit(0); + case 'a': + address = EARGF(usage()); + }ARGEND; + + cmd = EARGF(usage()); + + if(!address) + fatal("$WMII_ADDRESS not set\n"); + + client = ixp_mount(address); + if(client == nil) + fatal("%s\n", errstr); + + if(!strcmp(cmd, "create")) + ret = xcreate(argc, argv); + else if(!strcmp(cmd, "ls")) + ret = xls(argc, argv); + else if(!strcmp(cmd, "read")) + ret = xread(argc, argv); + else if(!strcmp(cmd, "remove")) + ret = xremove(argc, argv); + else if(!strcmp(cmd, "write")) + ret = xwrite(argc, argv); + else if(!strcmp(cmd, "xwrite")) + ret = xawrite(argc, argv); + else + usage(); + + ixp_unmount(client); + return ret; +} diff --git a/cmd/wmiistartrc.sh b/cmd/wmiistartrc.sh @@ -0,0 +1,13 @@ +#!/bin/sh -f +# start wmiirc + +export WMII_CONFPATH="$HOME/.wmii-CONFVERSION:CONFPREFIX/wmii-CONFVERSION" + +if wmii9rc; then + WMIIRC=`PATH="$WMII_CONFPATH:$PATH" which rc.wmii` +else + WMIIRC=`PATH="$WMII_CONFPATH:$PATH" which wmiirc` +fi + +mkdir $HOME/.wmii-CONFVERSION 2>/dev/null && CONFPREFIX/wmii-CONFVERSION/welcome & +exec "$WMIIRC" $@ diff --git a/column.c b/column.c @@ -1,317 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <math.h> -#include <string.h> -#include "wmii.h" - -char * -str_of_column_mode(int mode) { - switch(mode) { - case Coldefault: return "default"; break; - case Colstack: return "stack"; break; - case Colmax: return "max"; break; - default: break; - } - return nil; -} - -int -column_mode_of_str(char *arg) { - if(!strncmp("default", arg, 8)) - return Coldefault; - if(!strncmp("stack", arg, 6)) - return Colstack; - if(!strncmp("max", arg, 4)) - return Colmax; - return -1; -} - -static void -scale_column(Area *a) { - Frame *f, **fp; - uint min_height, yoff, dy; - uint num_col, num_uncol; - uint col_h, uncol_h; - int surplus, i, j; - - if(!a->frame) - return; - - /* This works by comparing heights based on a surplus of their - * minimum size. We start by subtracting the minimum size, then - * scale the surplus, and add back the minimum size later. This - * is based on the size of the client, rather than the frame, so - * increment gaps can be equalized later */ - /* Frames that can't be accomodated are pushed to the floating layer */ - - min_height = labelh(&def.font); - col_h = labelh(&def.font); - uncol_h = min_height + frame_delta_h(); - - num_col = 0; - num_uncol = 0; - dy = 0; - for(f=a->frame; f; f=f->anext) - if(f->collapsed) - num_col++; - else - num_uncol++; - - surplus = a->rect.height; - surplus -= num_col * col_h; - surplus -= num_uncol * uncol_h; - if(surplus < 0) { - i = ceil((float)(-surplus)/(uncol_h - col_h)); - if(i >= num_uncol) - i = num_uncol - 1; - num_uncol -= i; - num_col += i; - surplus += i * (uncol_h - col_h); - } - if(surplus < 0) { - i = ceil((float)(-surplus)/col_h); - if(i > num_col) - i = num_col; - num_col -= i; - surplus += i * col_h; - } - - i = num_col - 1; - j = num_uncol - 1; - for(f=a->frame; f; f=f->anext) { - if(f == a->sel) - j++; - if(!f->collapsed) { - if(j < 0 && f != a->sel) - f->collapsed = True; - else { - if(f->crect.height <= min_height) - f->crect.height = 1; - else - f->crect.height -= min_height; - dy += f->crect.height; - } - j--; - } - } - for(fp=&a->frame; *fp;) { - f = *fp; - if(f == a->sel) - i++; - if(f->collapsed) { - if(i < 0 && f != a->sel) { - f->collapsed = False; - send_to_area(f->view->area, f); - continue; - } - i--; - } - fp=&f->anext; - } - - i = num_uncol; - for(f=a->frame; f; f=f->anext) { - f->rect.x = a->rect.x; - f->rect.width = a->rect.width; - if(!f->collapsed) { - i--; - f->rect.height = (float)f->crect.height / dy * surplus; - if(!i) - f->rect.height = surplus; - f->rect.height += min_height + frame_delta_h(); - apply_sizehints(f->client, &f->rect, False, True, NWEST); - - dy -= f->crect.height; - surplus -= f->rect.height - frame_delta_h() - min_height; - }else - f->rect.height = labelh(&def.font); - } - - yoff = a->rect.y; - i = num_uncol; - for(f=a->frame; f; f=f->anext) { - f->rect.y = yoff; - f->rect.x = a->rect.x; - f->rect.width = a->rect.width; - if(f->collapsed) - yoff += f->rect.height; - else{ - i--; - f->rect.height += surplus / num_uncol; - if(!i) - f->rect.height += surplus % num_uncol; - yoff += f->rect.height; - } - } -} - -void -arrange_column(Area *a, Bool dirty) { - Frame *f; - - if(a->floating || !a->frame) - return; - - switch(a->mode) { - case Coldefault: - for(f=a->frame; f; f=f->anext) { - f->collapsed = False; - if(dirty) - f->crect.height = 100; - } - break; - case Colstack: - for(f=a->frame; f; f=f->anext) - f->collapsed = (f != a->sel); - break; - case Colmax: - for(f=a->frame; f; f=f->anext) { - f->collapsed = False; - f->rect = a->rect; - } - goto resize; - default: - break; - } - scale_column(a); -resize: - if(a->view == screen->sel) { - restack_view(a->view); - resize_client(a->sel->client, &a->sel->rect); - for(f=a->frame; f; f=f->anext) - if(!f->collapsed && f != a->sel) - resize_client(f->client, &f->rect); - for(f=a->frame; f; f=f->anext) - if(f->collapsed && f != a->sel) - resize_client(f->client, &f->rect); - } -} - -static void -match_horiz(Area *a, XRectangle *r) { - Frame *f; - - for(f=a->frame; f; f=f->anext) { - f->rect.x = r->x; - f->rect.width = r->width; - resize_frame(f, &f->rect); - } -} - -void -resize_column(Client *c, XRectangle *new) { - Area *west, *east, *a; - Frame *north, *south, *f; - View *v; - BlitzAlign sticky; - uint min_height; - uint min_width; - - f = c->sel; - a = f->area; - v = a->view; - min_height = 2 * labelh(&def.font); - min_width = screen->rect.width/NCOL; - - for(west=v->area->next; west; west=west->next) - if(west->next == a) break; - east = a->next; - for(north=a->frame; north; north=north->anext) - if(north->anext == f) break; - south = f->anext; - /* validate (and trim if necessary) horizontal resize */ - sticky = get_sticky(&f->rect, new); - if(new->width < min_width) { - if(sticky & EAST) - new->x = r_east(&a->rect) - min_width; - new->width = min_width; - } - if(west && !(sticky & WEST)) { - if(new->x < 0 || new->x < (west->rect.x + min_width)) { - new->width -= (west->rect.x + min_width) - new->x; - new->x = west->rect.x + min_width; - } - } else { - new->width += new->x - a->rect.x; - new->x = a->rect.x; - } - if(east && !(sticky & EAST)) { - if(r_east(new) > r_east(&east->rect) - min_width) - new->width = r_east(&east->rect) - min_width - new->x; - } else - new->width = r_east(&a->rect) - new->x; - if(new->width < min_width) - goto AfterHorizontal; - /* horizontal resize */ - sticky = get_sticky(&a->rect, new); - if(west && !(sticky & WEST)) { - west->rect.width = new->x - west->rect.x; - a->rect.width += a->rect.x - new->x; - a->rect.x = new->x; - match_horiz(a, &a->rect); - match_horiz(west, &west->rect); - //relax_column(west); - } - if(east && !(sticky & EAST)) { - east->rect.width -= r_east(new) - east->rect.x; - east->rect.x = r_east(new); - a->rect.width = r_east(new) - a->rect.x; - match_horiz(a, &a->rect); - match_horiz(east, &east->rect); - //relax_column(east); - } -AfterHorizontal: - /* skip vertical resize unless the column is in equal mode */ - if(a->mode != Coldefault) - goto AfterVertical; - /* validate (and trim if necessary) vertical resize */ - sticky = get_sticky(&f->rect, new); - if(new->height < min_height) { - if((f->rect.height < min_height) && sticky & (NORTH|SOUTH)) - goto AfterVertical; - if(sticky & SOUTH) - new->y = r_south(&f->rect) - min_height; - new->height = min_height; - } - if(north && !(sticky & NORTH)) - if(new->y < 0 || new->y < (north->rect.y + min_height)) { - new->height -= (north->rect.y + min_height) - new->y; - new->y = north->rect.y + min_height; - } - if(south && !(sticky & SOUTH)) { - if(r_south(new) > r_south(&south->rect) - min_height) - new->height = r_south(&south->rect) - min_height - new->y; - } - if(new->height < min_height) - goto AfterVertical; - /* vertical resize */ - if(north && !(sticky & NORTH)) { - north->rect.height = new->y - north->rect.y; - f->rect.height += f->rect.y - new->y; - f->rect.y = new->y; - resize_frame(north, &north->rect); - resize_frame(f, &f->rect); - } - if(south && !(sticky & SOUTH)) { - south->rect.height -= r_south(new) - south->rect.y; - south->rect.y = r_south(new); - f->rect.y = new->y; - f->rect.height = new->height; - resize_frame(f, &f->rect); - resize_frame(south, &south->rect); - } -AfterVertical: - arrange_column(a, False); - focus_view(screen, v); -} - -Area * -new_column(View *v, Area *pos, uint w) { - Area *a = create_area(v, pos, w); - if(!a) - return nil; - arrange_view(v); - return a; -} diff --git a/config.mk b/config.mk @@ -1,35 +1,38 @@ -# wmii version -VERSION = $$(hg tip --template 'hg{rev}' 2>/dev/null) -CONFVERSION = 3.5 - # Customize below to fit your system # paths PREFIX = /usr/local -CONFPREFIX = ${PREFIX}/etc -MANPREFIX = ${PREFIX}/share/man -AWKPATH = /usr/bin/awk -P9PATHS = /usr/local/plan9 /usr/local/9 /opt/plan9 /opt/9 /usr/plan9 /usr/9 +BIN = ${PREFIX}/bin +MAN = ${PREFIX}/share/man +ETC = ${PREFIX}/etc +LIBDIR = ${PREFIX}/lib +INCLUDE = ${PREFIX}/include -X11INC = /usr/X11R6/include -X11LIB = /usr/X11R6/lib +# Includes and libs +INCS = -I. -I${ROOT}/include -I${INCLUDE} -I/usr/include +LIBS = -L/usr/lib -lc -# includes and libs -INCS = -I. -I${PREFIX}/include -I/usr/include -I${X11INC} -LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lX11 -LIBIXP = -L${PREFIX}/lib -lixp - -# flags -#CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\" -#LDFLAGS = ${LIBS} +# Flags CFLAGS = -g -Wall ${INCS} -DVERSION=\"${VERSION}\" LDFLAGS = -g ${LIBS} +# Compiler +CC = cc -c +# Linker (Under normal circumstances, this should *not* be 'ld') +LD = cc +# Other +AR = ar cr +RANLIB = ranlib + +AWKPATH = /usr/bin/awk +P9PATHS = /usr/local/plan9 /usr/local/9 /opt/plan9 /opt/9 /usr/plan9 /usr/9 + +INCX11 = -I/usr/X11R6/include +LIBX11 = -L/usr/X11R6/lib -lX11 +LIBIXP = ${ROOT}/libixp/libixp.a + # Solaris #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" #LDFLAGS = ${LIBS} -R${PREFIX}/lib #LDFLAGS += -lsocket -lnsl #CFLAGS += -xtarget=ultra - -# compiler and linker -CC = cc diff --git a/draw.c b/draw.c @@ -1,227 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * See LICENSE file for license details. - */ -#include <stdlib.h> -#include <string.h> -#include "wmii.h" - -uint -textwidth_l(BlitzFont *font, char *text, uint len) { - if(font->set) { - XRectangle r; - XmbTextExtents(font->set, text, len, &r, nil); - return r.width; - } - return XTextWidth(font->xfont, text, len); -} - -uint -textwidth(BlitzFont *font, char *text) { - return textwidth_l(font, text, strlen(text)); -} - -void -loadfont(Blitz *blitz, BlitzFont *font) { - char *fontname = font->fontstr; - char **missing = nil, *def = "?"; - int n; - - if(font->set) - XFreeFontSet(blitz->dpy, font->set); - font->set = XCreateFontSet(blitz->dpy, fontname, &missing, &n, &def); - if(missing) { - while(n--) - fprintf(stderr, "wmii: missing fontset for '%s': %s\n", - fontname, missing[n]); - XFreeStringList(missing); - } - if(font->set) { - XFontSetExtents *font_extents; - XFontStruct **xfonts; - char **font_names; - - font->ascent = font->descent = 0; - font_extents = XExtentsOfFontSet(font->set); - XFontsOfFontSet(font->set, &xfonts, &font_names); - font->ascent = xfonts[0]->ascent; - font->descent = xfonts[0]->descent; - } - else { - if(font->xfont) - XFreeFont(blitz->dpy, font->xfont); - font->xfont = nil; - font->xfont = XLoadQueryFont(blitz->dpy, fontname); - if (!font->xfont) { - if(!strncmp(fontname, BLITZ_FONT, sizeof(BLITZ_FONT))) - fatal("cannot load font: %s", - BLITZ_FONT); - free(font->fontstr); - font->fontstr = estrdup(BLITZ_FONT); - loadfont(blitz, font); - return; - } - font->ascent = font->xfont->ascent; - font->descent = font->xfont->descent; - } - font->height = font->ascent + font->descent; -} - -uint -labelh(BlitzFont *font) { - return font->height + 2; -} - -void -draw_tile(BlitzBrush *b) { - drawbg(b->blitz->dpy, b->drawable, b->gc, &b->rect, - b->color, True, b->border); -} - -void -draw_border(BlitzBrush *b) { - drawbg(b->blitz->dpy, b->drawable, b->gc, &b->rect, - b->color, False, b->border); -} - -void -draw_label(BlitzBrush *b, char *text) { - uint x, y, w, h, len; - Bool shortened = False; - static char buf[2048]; - XRectangle r = {0}; - XGCValues gcv; - - draw_tile(b); - if(!text) - return; - shortened = 0; - strncpy(buf, text, sizeof(buf)); - len = strlen(buf); - gcv.foreground = b->color.fg; - gcv.background = b->color.bg; - h = b->font->ascent + b->font->descent; - y = b->rect.y + b->rect.height / 2 - h / 2 + b->font->ascent; - /* shorten text if necessary */ - while(len - && (w = textwidth(b->font, buf)) > b->rect.width - (b->font->height & ~1)) { - buf[--len] = 0; - shortened = True; - } - if(!len) - return; - if(w > b->rect.width) - return; - /* mark shortened info in the string */ - if(shortened) { - if (len > 3) - buf[len - 3] = '.'; - if (len > 2) - buf[len - 2] = '.'; - if (len > 1) - buf[len - 1] = '.'; - } - - if(b->font->set) { - XmbTextExtents(b->font->set, text, len, &r, nil); - } - - switch (b->align) { - case EAST: - x = b->rect.x + b->rect.width - (w + (b->font->height / 2)); - break; - default: - x = b->rect.x + (b->font->height / 2) - r.x; - break; - } - if(b->font->set) { - XChangeGC(b->blitz->dpy, b->gc, GCForeground | GCBackground, &gcv); - XmbDrawImageString(b->blitz->dpy, b->drawable, b->font->set, b->gc, - x, y, buf, len); - } - else { - gcv.font = b->font->xfont->fid; - XChangeGC(b->blitz->dpy, b->gc, GCForeground | GCBackground | GCFont, &gcv); - XDrawImageString(b->blitz->dpy, b->drawable, b->gc, x, y, buf, len); - } -} - -void -drawbg(Display *dpy, Drawable drawable, GC gc, XRectangle *rect, - BlitzColor c, Bool fill, int border) -{ - if(fill) { - XSetForeground(dpy, gc, c.bg); - XFillRectangles(dpy, drawable, gc, rect, 1); - } - if(border) { - XSetLineAttributes(dpy, gc, border, LineSolid, CapButt, JoinMiter); - XSetForeground(dpy, gc, c.border); - XDrawRectangle(dpy, drawable, gc, rect->x + border / 2, rect->y + border / 2, - rect->width - border, rect->height - border); - } -} - -void -drawcursor(Display *dpy, Drawable drawable, GC gc, - int x, int y, uint h, BlitzColor c) -{ - XSegment s[5]; - - XSetForeground(dpy, gc, c.fg); - XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); - s[0].x1 = x - 1; - s[0].y1 = s[0].y2 = y; - s[0].x2 = x + 2; - s[1].x1 = x - 1; - s[1].y1 = s[1].y2 = y + 1; - s[1].x2 = x + 2; - s[2].x1 = s[2].x2 = x; - s[2].y1 = y; - s[2].y2 = y + h; - s[3].x1 = x - 1; - s[3].y1 = s[3].y2 = y + h; - s[3].x2 = x + 2; - s[4].x1 = x - 1; - s[4].y1 = s[4].y2 = y + h - 1; - s[4].x2 = x + 2; - XDrawSegments(dpy, drawable, gc, s, 5); -} - -static ulong -xloadcolor(Blitz *blitz, char *colstr) { - XColor color; - char col[8]; - - strncpy(col, colstr, sizeof(col)); - col[7] = 0; - XAllocNamedColor(blitz->dpy, - DefaultColormap(blitz->dpy, blitz->screen), col, &color, &color); - return color.pixel; -} - -int -loadcolor(Blitz *blitz, BlitzColor *c) { - if(!c->colstr || strlen(c->colstr) != 23) - return -1; - c->fg = xloadcolor(blitz, &c->colstr[0]); - c->bg = xloadcolor(blitz, &c->colstr[8]); - c->border = xloadcolor(blitz, &c->colstr[16]); - return 0; -} - -char * -parse_colors(char **buf, int *buflen, BlitzColor *col) { - uint i; - if(*buflen < 23 || 3 != sscanf(*buf, "#%06x #%06x #%06x", &i,&i,&i)) - return "bad value"; - (*buflen) -= 23; - bcopy(*buf, col->colstr, 23); - loadcolor(&blz, col); - - (*buf) += 23; - if(**buf == '\n' || **buf == ' ') { - (*buf)++; - (*buflen)--; - } - return nil; -} diff --git a/event.c b/event.c @@ -1,448 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <X11/keysym.h> -#include "wmii.h" -#include "printevent.h" - -void -dispatch_event(XEvent *e) { - if(handler[e->type]) - handler[e->type](e); -} - -uint -flushevents(long event_mask, Bool dispatch) { - XEvent ev; - uint n = 0; - - while(XCheckMaskEvent(blz.dpy, event_mask, &ev)) { - if(dispatch) - dispatch_event(&ev); - n++; - } - return n; -} - -static void -buttonrelease(XEvent *e) { - XButtonPressedEvent *ev; - Frame *f; - Bar *b; - - ev = &e->xbutton; - if(ev->window == screen->barwin) { - for(b=screen->bar[BarLeft]; b; b=b->next) - if(ptinrect(ev->x, ev->y, &b->brush.rect)) { - write_event("LeftBarClick %d %s\n", ev->button, b->name); - return; - } - for(b=screen->bar[BarRight]; b; b=b->next) - if(ptinrect(ev->x, ev->y, &b->brush.rect)) { - write_event("RightBarClick %d %s\n", ev->button, b->name); - return; - } - } - else if((f = frame_of_win(ev->window))) - write_event("ClientClick 0x%x %d\n", f->client->win, ev->button); -} - -static void -buttonpress(XEvent *e) { - XButtonPressedEvent *ev; - Frame *f; - - ev = &e->xbutton; - if((f = frame_of_win(ev->window))) { - if((ev->state & def.mod) == def.mod) { - switch(ev->button) { - case Button1: - do_mouse_resize(f->client, False, CENTER); - focus(f->client, True); - frame_to_top(f); - focus(f->client, True); - break; - case Button3: - do_mouse_resize(f->client, False, - quadrant(&f->rect, ev->x_root, ev->y_root)); - frame_to_top(f); - focus(f->client, True); - break; - default: break; - XAllowEvents(blz.dpy, ReplayPointer, ev->time); - } - }else{ - if(ev->button == Button1) { - if(frame_to_top(f)) - restack_view(f->view); - - if(ptinrect(ev->x, ev->y, &f->grabbox)) - do_mouse_resize(f->client, True, CENTER); - else if(!ev->subwindow && !ptinrect(ev->x, ev->y, &f->titlebar)) - do_mouse_resize(f->client, False, quadrant(&f->rect, ev->x_root, ev->y_root)); - - if(f->client != sel_client()) - focus(f->client, True); - } - if(ev->subwindow) - XAllowEvents(blz.dpy, ReplayPointer, ev->time); - else { - /* Ungrab so a menu can receive events before the button is released */ - XUngrabPointer(blz.dpy, ev->time); - XSync(blz.dpy, False); - - write_event("ClientMouseDown 0x%x %d\n", f->client->win, ev->button); - } - } - }else - XAllowEvents(blz.dpy, ReplayPointer, ev->time); -} - -static void -configurerequest(XEvent *e) { - XConfigureRequestEvent *ev; - XWindowChanges wc; - XRectangle *frect; - Client *c; - Frame *f; - - ev = &e->xconfigurerequest; - c = client_of_win(ev->window); - if(c) { - f = c->sel; - gravitate_client(c, True); - if(ev->value_mask & CWX) - c->rect.x = ev->x; - if(ev->value_mask & CWY) - c->rect.y = ev->y; - if(ev->value_mask & CWWidth) - c->rect.width = ev->width; - if(ev->value_mask & CWHeight) - c->rect.height = ev->height; - if(ev->value_mask & CWBorderWidth) - c->border = ev->border_width; - gravitate_client(c, False); - - if((c->rect.height == screen->rect.height) - && (c->rect.width == screen->rect.width)) { - c->fullscreen = True; - if(c->sel) { - if(!c->sel->area->floating) - send_to_area(c->sel->view->area, c->sel); - focus_client(c); - restack_view(c->sel->view); - } - } - - if(c->sel->area->floating) - frect=&c->sel->rect; - else - frect=&c->sel->revert; - - *frect = c->rect; - frect->y -= labelh(&def.font); - frect->x -= def.border; - frect->width += 2 * def.border; - frect->height += frame_delta_h(); - - if(c->sel->area->floating || c->fullscreen) - resize_client(c, frect); - else - configure_client(c); - }else{ - wc.x = ev->x; - wc.y = ev->y; - wc.width = ev->width; - wc.height = ev->height; - wc.border_width = ev->border_width; - wc.sibling = ev->above; - wc.stack_mode = ev->detail; - ev->value_mask &= ~(CWStackMode|CWSibling); - XConfigureWindow(blz.dpy, ev->window, ev->value_mask, &wc); - XSync(blz.dpy, False); - } -} - -static void -destroynotify(XEvent *e) { - XDestroyWindowEvent *ev; - Client *c; - - ev = &e->xdestroywindow; - if((c = client_of_win(ev->window))) - destroy_client(c); -} - -static void -enternotify(XEvent *e) { - XCrossingEvent *ev; - Client *c; - Frame *f; - - ev = &e->xcrossing; - if(ev->mode != NotifyNormal) - return; - - if((c = client_of_win(ev->window))) { - if(ev->detail != NotifyInferior) { - if(screen->focus != c) { - if(verbose) fprintf(stderr, "enter_notify(c) => %s\n", c->name); - focus(c, False); - } - set_cursor(c, cursor[CurNormal]); - }else if(verbose) fprintf(stderr, "enter_notify(c[NotifyInferior]) => %s\n", c->name); - } - else if((f = frame_of_win(ev->window))) { - if(screen->focus != c) { - if(verbose) fprintf(stderr, "enter_notify(f) => %s\n", f->client->name); - if(f->area->floating || !f->collapsed) - focus(f->client, False); - } - set_frame_cursor(f, ev->x, ev->y); - } - else if(ev->window == blz.root) { - sel_screen = True; - draw_frames(); - } -} - -static void -leavenotify(XEvent *e) { - XCrossingEvent *ev; - - ev = &e->xcrossing; - if((ev->window == blz.root) && !ev->same_screen) { - sel_screen = True; - draw_frames(); - } -} - -void -print_focus(Client *c, char *to) { - if(verbose) { - fprintf(stderr, "screen->focus: %p => %p\n", - screen->focus, c); - fprintf(stderr, "\t%s => %s\n", - screen->focus ? screen->focus->name : "<nil>", - to); - } -} - -static void -focusin(XEvent *e) { - XFocusChangeEvent *ev; - Client *c, *old; - XEvent me; - - ev = &e->xfocus; - /* Yes, we're focusing in on nothing, here. */ - if(ev->detail == NotifyDetailNone) { - XSetInputFocus(blz.dpy, screen->barwin, RevertToParent, CurrentTime); - return; - } - - if(!((ev->detail == NotifyNonlinear) - ||(ev->detail == NotifyNonlinearVirtual) - ||(ev->detail == NotifyVirtual) - ||(ev->detail == NotifyInferior) - ||(ev->detail == NotifyAncestor))) - return; - if((ev->mode == NotifyWhileGrabbed) - && (screen->hasgrab != &c_root)) - return; - - old = screen->focus; - c = client_of_win(ev->window); - if(c) { - print_focus(c, c->name); - if(ev->mode == NotifyGrab) - screen->hasgrab = c; - screen->focus = c; - if(c != old) { - update_client_grab(c); - if(c->sel) - draw_frame(c->sel); - if(old && old->sel) - draw_frame(old->sel); - } - }else if(ev->window == screen->barwin) { - print_focus(nil, "<nil>"); - screen->focus = nil; - }else if(ev->mode == NotifyGrab) { - if(ev->window == blz.root) - if(XCheckMaskEvent(blz.dpy, KeyPressMask, &me)) { - /* wmii has grabbed focus */ - screen->hasgrab = &c_root; - dispatch_event(&me); - return; - } - /* Some unmanaged window has grabbed focus */ - if((c = screen->focus)) { - print_focus(&c_magic, "<magic>"); - screen->focus = &c_magic; - if(c->sel) - draw_frame(c->sel); - } - } -} - -static void -focusout(XEvent *e) { - XFocusChangeEvent *ev; - Client *c; - - ev = &e->xfocus; - if(!((ev->detail == NotifyNonlinear) - ||(ev->detail == NotifyNonlinearVirtual))) - return; - if(ev->mode == NotifyUngrab) - screen->hasgrab = nil; - - c = client_of_win(ev->window); - if(c) { - if((ev->mode == NotifyWhileGrabbed) - && (screen->hasgrab != &c_root)) { - if((screen->focus) - && (screen->hasgrab != screen->focus)) - screen->hasgrab = screen->focus; - if(screen->hasgrab == c) - return; - }else if(ev->mode != NotifyGrab) { - if(screen->focus == c) { - print_focus(&c_magic, "<magic>"); - screen->focus = &c_magic; - } - update_client_grab(c); - if(c->sel) - draw_frame(c->sel); - } - } -} - -static void -expose(XEvent *e) { - XExposeEvent *ev; - static Frame *f; - - ev = &e->xexpose; - if(ev->count == 0) { - if(ev->window == screen->barwin) - draw_bar(screen); - else if((f = frame_of_win(ev->window))) - draw_frame(f); - } -} - -static void -keypress(XEvent *e) { - XKeyEvent *ev; - - ev = &e->xkey; - ev->state &= valid_mask; - if(ev->window == blz.root) - kpress(blz.root, ev->state, (KeyCode) ev->keycode); -} - -static void -mappingnotify(XEvent *e) { - XMappingEvent *ev; - - ev = &e->xmapping; - XRefreshKeyboardMapping(ev); - if(ev->request == MappingKeyboard) - update_keys(); -} - -static void -maprequest(XEvent *e) { - XMapRequestEvent *ev; - static XWindowAttributes wa; - - ev = &e->xmaprequest; - if(!XGetWindowAttributes(blz.dpy, ev->window, &wa)) - return; - if(wa.override_redirect) { - XSelectInput(blz.dpy, ev->window, - (StructureNotifyMask | PropertyChangeMask)); - return; - } - if(!client_of_win(ev->window)) - manage_client(create_client(ev->window, &wa)); -} - -static void -motionnotify(XEvent *e) { - XMotionEvent *ev; - Frame *f; - - ev = &e->xmotion; - if((f = frame_of_win(ev->window))) - set_frame_cursor(f, ev->x, ev->y); -} - -static void -propertynotify(XEvent *e) { - XPropertyEvent *ev; - Client *c; - - ev = &e->xproperty; - if(ev->state == PropertyDelete) - return; /* ignore */ - if((c = client_of_win(ev->window))) - prop_client(c, ev->atom); -} - -static void -mapnotify(XEvent *e) { - XMapEvent *ev; - Client *c; - - ev = &e->xmap; - if((c = client_of_win(ev->window))) - if(c == sel_client()) - focus_client(c); -} - -static void -unmapnotify(XEvent *e) { - XUnmapEvent *ev; - Client *c; - - ev = &e->xunmap; - if((c = client_of_win(ev->window))) - if(ev->send_event || (c->unmapped-- == 0)) - destroy_client(c); -} - -void (*handler[LASTEvent]) (XEvent *) = { - [ButtonPress] = buttonpress, - [ButtonRelease] = buttonrelease, - [ConfigureRequest]=configurerequest, - [DestroyNotify] = destroynotify, - [EnterNotify] = enternotify, - [Expose] = expose, - [FocusIn] = focusin, - [FocusOut] = focusout, - [KeyPress] = keypress, - [LeaveNotify] = leavenotify, - [MapNotify] = mapnotify, - [MapRequest] = maprequest, - [MappingNotify] = mappingnotify, - [MotionNotify] = motionnotify, - [PropertyNotify]= propertynotify, - [UnmapNotify] = unmapnotify, - -}; - -void -check_x_event(IXPConn *c) { - XEvent ev; - while(XPending(blz.dpy)) { - XNextEvent(blz.dpy, &ev); - if(verbose) - printevent(&ev); - dispatch_event(&ev); - XPending(blz.dpy); - } -} diff --git a/frame.c b/frame.c @@ -1,341 +0,0 @@ -/* Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include "wmii.h" - -Frame * -create_frame(Client *c, View *v) { - static ushort id = 1; - Frame *f = emallocz(sizeof(Frame)); - - f->id = id++; - f->client = c; - f->view = v; - if(c->sel) { - f->revert = c->sel->revert; - f->rect = c->sel->rect; - } - else{ - c->sel = f; - f->revert = f->rect = c->rect; - f->revert.width = f->rect.width += 2 * def.border; - f->revert.height = f->rect.height += frame_delta_h(); - } - f->collapsed = False; - - return f; -} - -void -remove_frame(Frame *f) { - Area *a; - Frame **ft; - - a = f->area; - for(ft = &a->frame; *ft; ft=&(*ft)->anext) - if(*ft == f) break; - *ft = f->anext; - - if(a->floating) { - for(ft = &a->stack; *ft; ft=&(*ft)->snext) - if(*ft == f) break; - *ft = f->snext; - } -} - -void -insert_frame(Frame *pos, Frame *f, Bool before) { - Frame *ft, **p; - Area *a = f->area; - - if(before) { - for(ft=a->frame; ft; ft=ft->anext) - if(ft->anext == pos) break; - pos=ft; - } - p = &a->frame; - if(pos) - p = &pos->anext; - f->anext = *p; - *p = f; - - if(a->floating) { - f->snext = a->stack; - a->stack = f; - } -} - -void -frame2client(XRectangle *r) { - r->width = max(r->width - def.border * 2, 1); - r->height = max(r->height - frame_delta_h(), 1); -} - -void -client2frame(XRectangle *r) { - r->width += def.border * 2; - r->height += frame_delta_h(); -} - -void -resize_frame(Frame *f, XRectangle *r) { - BlitzAlign stickycorner; - Client *c; - - c = f->client; - stickycorner = get_sticky(&f->rect, r); - - f->rect = *r; - f->crect = *r; - apply_sizehints(c, &f->crect, f->area->floating, True, stickycorner); - - if(f->area->floating) - f->rect = f->crect; - - frame2client(&f->crect); - - if(f->crect.height < labelh(&def.font)) - f->collapsed = True; - else - f->collapsed = False; - - if(f->crect.width < labelh(&def.font)) { - f->rect.width = frame_delta_h(); - f->collapsed = True; - } - - if(f->collapsed) { - f->rect.height = labelh(&def.font); - f->crect = f->rect; - } - f->crect.y = labelh(&def.font); - f->crect.x = (f->rect.width - f->crect.width) / 2; - - - if(f->area->floating) { - if(c->fullscreen) { - f->crect.width = screen->rect.width; - f->crect.height = screen->rect.height; - f->rect = f->crect; - client2frame(&f->rect); - f->rect.x = -def.border; - f->rect.y = -labelh(&def.font); - }else - check_frame_constraints(&f->rect); - } -} - -void -set_frame_cursor(Frame *f, int x, int y) { - XRectangle r; - Cursor cur; - - if(!ptinrect(x, y, &f->titlebar) - &&!ptinrect(x, y, &f->crect)) { - r = f->rect; - r.x = 0; - r.y = 0; - cur = cursor_of_quad(quadrant(&r, x, y)); - set_cursor(f->client, cur); - }else - set_cursor(f->client, cursor[CurNormal]); -} - -Bool -frame_to_top(Frame *f) { - Frame **tf; - Area *a; - - a = f->area; - if(!a->floating || f == a->stack) - return False; - for(tf=&a->stack; *tf; tf=&(*tf)->snext) - if(*tf == f) break; - *tf = f->snext; - f->snext = a->stack; - a->stack = f; - update_client_grab(f->client); - return True; -} - -void -swap_frames(Frame *fa, Frame *fb) { - XRectangle trect; - Area *a; - Frame **fp_a, **fp_b, *ft; - - if(fa == fb) return; - - a = fa->area; - for(fp_a = &a->frame; *fp_a; fp_a = &(*fp_a)->anext) - if(*fp_a == fa) break; - a = fb->area; - for(fp_b = &a->frame; *fp_b; fp_b = &(*fp_b)->anext) - if(*fp_b == fb) break; - - if(fa->anext == fb) { - *fp_a = fb; - fa->anext = fb->anext; - fb->anext = fa; - } else if(fb->anext == fa) { - *fp_b = fa; - fb->anext = fa->anext; - fa->anext = fb; - } else { - *fp_a = fb; - *fp_b = fa; - ft = fb->anext; - fb->anext = fa->anext; - fa->anext = ft; - } - - if(fb->area->sel == fb) - fb->area->sel = fa; - if(fa->area->sel == fa) - fa->area->sel = fb; - - fb->area = fa->area; - fa->area = a; - - trect = fa->rect; - fa->rect = fb->rect; - fb->rect = trect; -} - -void -focus_frame(Frame *f, Bool restack) { - Frame *old, *old_in_a; - View *v; - Area *a, *old_a; - - a = f->area; - v = f->view; - old = v->sel->sel; - old_a = v->sel; - old_in_a = a->sel; - - a->sel = f; - - if(a != old_a) - focus_area(f->area); - - if(v != screen->sel) - return; - - focus_client(f->client); - - if(!a->floating - && ((a->mode == Colstack) || (a->mode == Colmax))) - arrange_column(a, False); - - if((f != old) - && (f->area == old_a)) - write_event("ClientFocus 0x%x\n", f->client->win); - - if(restack) - restack_view(v); -} - -int -frame_delta_h() { - return def.border + labelh(&def.font); -} - -void -draw_frame(Frame *f) { - BlitzBrush br = { 0 }; - Frame *tf; - - if(f->view != screen->sel) - return; - - br.blitz = &blz; - br.font = &def.font; - br.drawable = pmap; - br.gc = f->client->gc; - if(f->client == screen->focus) - br.color = def.focuscolor; - else - br.color = def.normcolor; - if(!f->area->floating && f->area->mode == Colmax) - for(tf = f->area->frame; tf; tf=tf->anext) - if(tf->client == screen->focus) { - br.color = def.focuscolor; - break; - } - - br.rect = f->rect; - br.rect.x = 0; - br.rect.y = 0; - draw_tile(&br); - - br.rect.x += def.font.height - 3; - br.rect.width -= br.rect.x; - br.rect.height = labelh(&def.font); - draw_label(&br, f->client->name); - - br.border = 1; - br.rect.width += br.rect.x; - br.rect.x = 0; - f->titlebar.x = br.rect.x + 3; - f->titlebar.height = br.rect.height - 3; - f->titlebar.y = br.rect.y + 3; - f->titlebar.width = br.rect.width - 6; - draw_border(&br); - br.rect.height = f->rect.height; - if(def.border) - draw_border(&br); - - if(f->client->urgent) - br.color.bg = br.color.fg; - br.rect.x = 2; - br.rect.y = 2; - br.rect.height = labelh(&def.font) - 4; - br.rect.width = def.font.height - 3; - f->grabbox = br.rect; - draw_tile(&br); - - XCopyArea( - /* display */ blz.dpy, - /* src */ pmap, - /* dest */ f->client->framewin, - /* gc */ f->client->gc, - /* x, y */ 0, 0, - /* width */ f->rect.width, - /* height */ f->rect.height, - /* dest_x */ 0, - /* dest_y */ 0 - ); - XSync(blz.dpy, False); -} - -void -draw_frames() { - Client *c; - - for(c=client; c; c=c->next) - if(c->sel && c->sel->view == screen->sel) - draw_frame(c->sel); -} - -void -check_frame_constraints(XRectangle *rect) { - int max_height; - int barheight; - - barheight = screen->brect.height; - max_height = screen->rect.height - barheight; - - if(rect->height > max_height) - rect->height = max_height; - if(rect->width > screen->rect.width) - rect->width = screen->rect.width; - if(rect->x + barheight > screen->rect.width) - rect->x = screen->rect.width - barheight; - if(rect->y + barheight > max_height) - rect->y = max_height - barheight; - if(r_east(rect) < barheight) - rect->x = barheight - rect->width; - if(r_south(rect) < barheight) - rect->y = barheight - rect->height; -} diff --git a/fs.c b/fs.c @@ -1,966 +0,0 @@ -/* Copyright ©2006 Kris Maglione <fbsdaemon at gmail dot com> - * See LICENSE file for license details. - */ -#include <assert.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include "wmii.h" - - -/* Datatypes: */ -/**************/ -typedef struct Dirtab Dirtab; -struct Dirtab { - char *name; - uchar qtype; - uint type; - uint perm; -}; - -typedef struct FidLink FidLink; -struct FidLink { - FidLink *next; - Fid *fid; -}; - -typedef struct FileId FileId; -struct FileId { - FileId *next; - union { - void *ref; - char *buf; - Bar *bar; - Bar **bar_p; - View *view; - Client *client; - Ruleset *rule; - BlitzColor *col; - } content; - uint id; - uint index; - Dirtab tab; - ushort nref; -}; - -/* Constants */ -/*************/ -enum { /* Dirs */ - FsRoot, FsDClient, FsDClients, FsDBars, - FsDTag, FsDTags, - /* Files */ - FsFBar, FsFCctl, FsFColRules, - FsFCtags, FsFEvent, FsFKeys, FsFRctl, - FsFTagRules, FsFTctl, FsFTindex, - FsFprops -}; - -/* Error messages */ -static char - Enoperm[] = "permission denied", - Enofile[] = "file not found", - Ebadvalue[] = "bad value", - Einterrupted[] = "interrupted", - Ebadcmd[] = "bad command"; - -/* Macros */ -#define QID(t, i) (((vlong)((t)&0xFF)<<32)|((i)&0xFFFFFFFF)) - -/* Global Vars */ -/***************/ -FileId *free_fileid; -P9Req *pending_event_reads; -P9Req *outgoing_event_reads; -FidLink *pending_event_fids; -P9Srv p9srv = { - .open= fs_open, - .walk= fs_walk, - .read= fs_read, - .stat= fs_stat, - .write= fs_write, - .clunk= fs_clunk, - .flush= fs_flush, - .attach=fs_attach, - .create=fs_create, - .remove=fs_remove, - .freefid=fs_freefid -}; - -/* ad-hoc file tree. Empty names ("") indicate dynamic entries to be filled - * in by lookup_file */ -static Dirtab -dirtab_root[]= {{".", P9QTDIR, FsRoot, 0500|P9DMDIR }, - {"rbar", P9QTDIR, FsDBars, 0700|P9DMDIR }, - {"lbar", P9QTDIR, FsDBars, 0700|P9DMDIR }, - {"client", P9QTDIR, FsDClients, 0500|P9DMDIR }, - {"tag", P9QTDIR, FsDTags, 0500|P9DMDIR }, - {"ctl", P9QTAPPEND, FsFRctl, 0600|P9DMAPPEND }, - {"colrules", P9QTFILE, FsFColRules, 0600 }, - {"event", P9QTFILE, FsFEvent, 0600 }, - {"keys", P9QTFILE, FsFKeys, 0600 }, - {"tagrules", P9QTFILE, FsFTagRules, 0600 }, - {nil}}, -dirtab_clients[]={{".", P9QTDIR, FsDClients, 0500|P9DMDIR }, - {"", P9QTDIR, FsDClient, 0500|P9DMDIR }, - {nil}}, -dirtab_client[]= {{".", P9QTDIR, FsDClient, 0500|P9DMDIR }, - {"ctl", P9QTAPPEND, FsFCctl, 0600|P9DMAPPEND }, - {"tags", P9QTFILE, FsFCtags, 0600 }, - {"props", P9QTFILE, FsFprops, 0400 }, - {nil}}, -dirtab_bars[]= {{".", P9QTDIR, FsDBars, 0700|P9DMDIR }, - {"", P9QTFILE, FsFBar, 0600 }, - {nil}}, -dirtab_tags[]= {{".", P9QTDIR, FsDTags, 0500|P9DMDIR }, - {"", P9QTDIR, FsDTag, 0500|P9DMDIR }, - {nil}}, -dirtab_tag[]= {{".", P9QTDIR, FsDTag, 0500|P9DMDIR }, - {"ctl", P9QTAPPEND, FsFTctl, 0600|P9DMAPPEND }, - {"index", P9QTFILE, FsFTindex, 0400 }, - {nil}}; -/* Writing the lists separately and using an array of their references - * removes the need for casting and allows for C90 conformance, - * since otherwise we would need to use compound literals */ -static Dirtab *dirtab[] = { - [FsRoot] = dirtab_root, - [FsDBars] = dirtab_bars, - [FsDClients] = dirtab_clients, - [FsDClient] = dirtab_client, - [FsDTags] = dirtab_tags, - [FsDTag] = dirtab_tag, -}; - -/* Utility Functions */ -/*********************/ - -/* get_file/free_file save and reuse old FileId structs - * since so many of them are needed for so many - * purposes */ -static FileId * -get_file() { - FileId *temp; - if(!free_fileid) { - uint i = 15; - temp = emallocz(sizeof(FileId) * i); - for(; i; i--) { - temp->next = free_fileid; - free_fileid = temp++; - } - } - temp = free_fileid; - free_fileid = temp->next; - temp->nref = 1; - temp->next = nil; - return temp; -} - -static void -free_file(FileId *f) { - if(--f->nref) - return; - free(f->tab.name); - f->next = free_fileid; - free_fileid = f; -} - -/* This function's name belies it's true purpose. It increases - * the reference counts of the FileId list */ -static void -clone_files(FileId *f) { - for(; f; f=f->next) - assert(f->nref++); -} - -/* This should be moved to libixp */ -static void -write_buf(P9Req *r, char *buf, uint len) { - if(r->ifcall.offset >= len) - return; - - len -= r->ifcall.offset; - if(len > r->ifcall.count) - len = r->ifcall.count; - r->ofcall.data = emalloc(len); - memcpy(r->ofcall.data, buf + r->ifcall.offset, len); - r->ofcall.count = len; -} - -/* This should be moved to libixp */ -void -write_to_buf(P9Req *r, void *buf, uint *len, uint max) { - uint offset, count; - - offset = (r->fid->omode&P9OAPPEND) ? *len : r->ifcall.offset; - if(offset > *len || r->ifcall.count == 0) { - r->ofcall.count = 0; - return; - } - - count = r->ifcall.count; - if(max && (count > max - offset)) - count = max - offset; - - *len = offset + count; - - if(max == 0) { - *(void **)buf = erealloc(*(void **)buf, *len + 1); - buf = *(void **)buf; - } - - memcpy((uchar*)buf + offset, r->ifcall.data, count); - r->ofcall.count = count; - ((char *)buf)[offset+count] = '\0'; -} - -/* This should be moved to libixp */ -void -data_to_cstring(P9Req *r) { - uint i; - i = r->ifcall.count; - if(!i || r->ifcall.data[i - 1] != '\n') - r->ifcall.data = erealloc(r->ifcall.data, ++i); - assert(r->ifcall.data); - r->ifcall.data[i - 1] = '\0'; -} - -char * -message_root(char *message) -{ - uint n; - - if(!strchr(message, ' ')) { - snprintf(buffer, BUFFER_SIZE, "%s ", message); - message = buffer; - } - if(!strcmp(message, "quit ")) - srv.running = 0; - else if(!strncmp(message, "exec ", 5)) { - srv.running = 0; - execstr = estrdup(&message[5]); - message += strlen(message); - } - else if(!strncmp(message, "view ", 5)) - select_view(&message[5]); - else if(!strncmp(message, "selcolors ", 10)) { - fprintf(stderr, "wmii: warning: selcolors have been removed\n"); - return Ebadcmd; - } - else if(!strncmp(message, "focuscolors ", 12)) { - message += 12; - n = strlen(message); - return parse_colors(&message, (int *)&n, &def.focuscolor); - } - else if(!strncmp(message, "normcolors ", 11)) { - message += 11; - n = strlen(message); - return parse_colors(&message, (int *)&n, &def.normcolor); - } - else if(!strncmp(message, "font ", 5)) { - message += 5; - free(def.font.fontstr); - def.font.fontstr = estrdup(message); - loadfont(&blz, &def.font); - resize_bar(screen); - } - else if(!strncmp(message, "border ", 7)) { - message += 7; - n = (uint)strtol(message, &message, 10); - if(*message) - return Ebadvalue; - def.border = n; - } - else if(!strncmp(message, "grabmod ", 8)) { - message += 8; - ulong mod; - mod = mod_key_of_str(message); - if(!(mod & (Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))) - return Ebadvalue; - strncpy(def.grabmod, message, sizeof(def.grabmod)); - def.mod = mod; - if(view) - restack_view(screen->sel); - } - else - return Ebadcmd; - return nil; -} - -char * -read_root_ctl() { - uint i = 0; - if(screen->sel) - i += snprintf(&buffer[i], (BUFFER_SIZE - i), "view %s\n", screen->sel->name); - i += snprintf(&buffer[i], (BUFFER_SIZE - i), "focuscolors %s\n", def.focuscolor.colstr); - i += snprintf(&buffer[i], (BUFFER_SIZE - i), "normcolors %s\n", def.normcolor.colstr); - i += snprintf(&buffer[i], (BUFFER_SIZE - i), "font %s\n", def.font.fontstr); - i += snprintf(&buffer[i], (BUFFER_SIZE - i), "grabmod %s\n", def.grabmod); - i += snprintf(&buffer[i], (BUFFER_SIZE - i), "border %d\n", def.border); - return buffer; -} - - -void -respond_event(P9Req *r) { - FileId *f = r->fid->aux; - if(f->content.buf) { - r->ofcall.data = (void *)f->content.buf; - r->ofcall.count = strlen(f->content.buf); - respond(r, nil); - f->content.buf = nil; - }else{ - r->aux = pending_event_reads; - pending_event_reads = r; - } -} - -void -write_event(char *format, ...) { - uint len, slen; - va_list ap; - FidLink *f; - FileId *fi; - P9Req *req; - - va_start(ap, format); - vsnprintf(buffer, BUFFER_SIZE, format, ap); - va_end(ap); - if(!(len = strlen(buffer))) - return; - for(f=pending_event_fids; f; f=f->next) { - fi = f->fid->aux; - slen = fi->content.buf ? strlen(fi->content.buf) : 0; - fi->content.buf = (char *) erealloc(fi->content.buf, slen + len + 1); - (fi->content.buf)[slen] = '\0'; - strcat(fi->content.buf, buffer); - } - outgoing_event_reads = pending_event_reads; - pending_event_reads = nil; - while((req = outgoing_event_reads)) { - outgoing_event_reads = outgoing_event_reads->aux; - respond_event(req); - } -} - -static void -dostat(Stat *s, uint len, FileId *f) { - s->type = 0; - s->dev = 0; - s->qid.path = QID(f->tab.type, f->id); - s->qid.version = 0; - s->qid.type = f->tab.qtype; - s->mode = f->tab.perm; - s->atime = time(nil); - s->mtime = time(nil); - s->length = len; - s->name = f->tab.name; - s->uid = user; - s->gid = user; - s->muid = user; -} - -/* lookup_file */ -/***************/ -/* All lookups and directory organization should be performed through - * lookup_file, mostly through the dirtabs[] tree. */ -static FileId * -lookup_file(FileId *parent, char *name) -{ - FileId *ret, *file, **last; - Dirtab *dir; - Client *c; - View *v; - Bar *b; - uint id; - - if(!(parent->tab.perm & P9DMDIR)) - return nil; - dir = dirtab[parent->tab.type]; - last = &ret; - ret = nil; - for(; dir->name; dir++) { - /* Dynamic dirs */ - if(!*dir->name) { /* strlen(dir->name) == 0 */ - switch(parent->tab.type) { - case FsDClients: - if(!name || !strcmp(name, "sel")) { - if((c = sel_client())) { - file = get_file(); - *last = file; - last = &file->next; - file->content.client = c; - file->id = c->win; - file->index = c->win; - file->tab = *dir; - file->tab.name = estrdup("sel"); - }if(name) goto LastItem; - } - if(name) { - id = (uint)strtol(name, &name, 16); - if(*name) goto NextItem; - } - for(c=client; c; c=c->next) { - if(!name || c->win == id) { - file = get_file(); - *last = file; - last = &file->next; - file->content.client = c; - file->id = c->win; - file->index = c->win; - file->tab = *dir; - file->tab.name = emallocz(16); - snprintf(file->tab.name, 16, "0x%x", (uint)c->win); - if(name) goto LastItem; - } - } - break; - case FsDTags: - if(!name || !strcmp(name, "sel")) { - if(screen->sel) { - file = get_file(); - *last = file; - last = &file->next; - file->content.view = screen->sel; - file->id = screen->sel->id; - file->tab = *dir; - file->tab.name = estrdup("sel"); - }if(name) goto LastItem; - } - for(v=view; v; v=v->next) { - if(!name || !strcmp(name, v->name)) { - file = get_file(); - *last = file; - last = &file->next; - file->content.view = v; - file->id = v->id; - file->tab = *dir; - file->tab.name = estrdup(v->name); - if(name) goto LastItem; - } - } - break; - case FsDBars: - for(b=*parent->content.bar_p; b; b=b->next) { - if(!name || !strcmp(name, b->name)) { - file = get_file(); - *last = file; - last = &file->next; - file->content.bar = b; - file->id = b->id; - file->tab = *dir; - file->tab.name = estrdup(b->name); - if(name) goto LastItem; - } - } - break; - } - }else /* Static dirs */ - if(!name || !strcmp(name, dir->name)) { - file = get_file(); - *last = file; - last = &file->next; - file->id = 0; - file->content.ref = parent->content.ref; - file->index = parent->index; - file->tab = *dir; - file->tab.name = estrdup(file->tab.name); - /* Special considerations: */ - switch(file->tab.type) { - case FsDBars: - if(!strcmp(file->tab.name, "lbar")) - file->content.bar_p = &screen[0].bar[BarLeft]; - else - file->content.bar_p = &screen[0].bar[BarRight]; - break; - case FsFColRules: - file->content.rule = &def.colrules; - break; - case FsFTagRules: - file->content.rule = &def.tagrules; - break; - } - if(name) goto LastItem; - } - NextItem: - continue; - } -LastItem: - *last = nil; - return ret; -} - -Bool -verify_file(FileId *f) { - FileId *nf; - - if(!f->next) - return True; - if(verify_file(f->next)) { - nf = lookup_file(f->next, f->tab.name); - if(nf) { - free_file(nf); - return True; - } - } - return False; -} - -/* Service Functions */ -/*********************/ -void -fs_attach(P9Req *r) { - FileId *f = get_file(); - f->tab = dirtab[FsRoot][0]; - f->tab.name = estrdup("/"); - f->content.ref = nil; /* shut up valgrind */ - r->fid->aux = f; - r->fid->qid.type = f->tab.qtype; - r->fid->qid.path = QID(f->tab.type, 0); - r->ofcall.qid = r->fid->qid; - respond(r, nil); -} - -void -fs_walk(P9Req *r) { - FileId *f, *nf; - int i; - - f = r->fid->aux; - clone_files(f); - for(i=0; i < r->ifcall.nwname; i++) { - if(!strcmp(r->ifcall.wname[i], "..")) { - if(f->next) { - nf=f; - f=f->next; - free_file(nf); - } - }else{ - nf = lookup_file(f, r->ifcall.wname[i]); - if(!nf) - break; - assert(!nf->next); - if(strcmp(r->ifcall.wname[i], ".")) { - nf->next = f; - f = nf; - } - } - r->ofcall.wqid[i].type = f->tab.qtype; - r->ofcall.wqid[i].path = QID(f->tab.type, f->id); - } - /* There should be a way to do this on freefid() */ - if(i < r->ifcall.nwname) { - while((nf = f)) { - f=f->next; - free_file(nf); - } - respond(r, Enofile); - return; - } - /* Remove refs for r->fid if no new fid */ - if(r->ifcall.fid == r->ifcall.newfid) { - nf = r->fid->aux; - r->fid->aux = f; - while((f = nf)) { - nf = nf->next; - free_file(f); - } - }else - r->newfid->aux = f; - r->ofcall.nwqid = i; - respond(r, nil); -} - -uint -fs_size(FileId *f) { - switch(f->tab.type) { - default: - return 0; - case FsFColRules: - case FsFTagRules: - return f->content.rule->size; - case FsFKeys: - return def.keyssz; - case FsFCtags: - return strlen(f->content.client->tags); - case FsFprops: - return strlen(f->content.client->props); - } -} - -void -fs_stat(P9Req *r) { - Stat s; - int size; - uchar *buf; - FileId *f = r->fid->aux; - - if(!verify_file(f)) { - respond(r, Enofile); - return; - } - - dostat(&s, fs_size(f), f); - r->ofcall.nstat = size = ixp_sizeof_stat(&s); - buf = emallocz(size); - r->ofcall.stat = buf; - ixp_pack_stat(&buf, &size, &s); - respond(r, nil); -} - -void -fs_read(P9Req *r) { - char *buf; - FileId *f, *tf; - int n, offset; - int size; - - offset = 0; - f = r->fid->aux; - - if(!verify_file(f)) { - respond(r, Enofile); - return; - } - - if(f->tab.perm & P9DMDIR && f->tab.perm & 0400) { - Stat s; - offset = 0; - size = r->ifcall.count; - buf = emallocz(size); - r->ofcall.data = buf; - tf = f = lookup_file(f, nil); - /* Note: f->tab.name == "." so we skip it */ - for(f=f->next; f; f=f->next) { - dostat(&s, fs_size(f), f); - n = ixp_sizeof_stat(&s); - if(offset >= r->ifcall.offset) { - if(size < n) - break; - ixp_pack_stat((uchar **)&buf, &size, &s); - } - offset += n; - } - while((f = tf)) { - tf=tf->next; - free_file(f); - } - r->ofcall.count = r->ifcall.count - size; - respond(r, nil); - return; - } - else{ - switch(f->tab.type) { - case FsFprops: - write_buf(r, f->content.client->props, strlen(f->content.client->props)); - respond(r, nil); - return; - case FsFColRules: - case FsFTagRules: - write_buf(r, f->content.rule->string, f->content.rule->size); - respond(r, nil); - return; - case FsFKeys: - write_buf(r, def.keys, def.keyssz); - respond(r, nil); - return; - case FsFCtags: - write_buf(r, f->content.client->tags, strlen(f->content.client->tags)); - respond(r, nil); - return; - case FsFTctl: - write_buf(r, f->content.view->name, strlen(f->content.view->name)); - respond(r, nil); - return; - case FsFBar: - write_buf(r, f->content.bar->buf, strlen(f->content.bar->buf)); - respond(r, nil); - return; - case FsFRctl: - buf = read_root_ctl(); - write_buf(r, buf, strlen(buf)); - respond(r, nil); - return; - case FsFCctl: - if(r->ifcall.offset) { - respond(r, nil); - return; - } - r->ofcall.data = emallocz(16); - n = snprintf(r->ofcall.data, 16, "0x%x", (uint)f->index); - assert(n >= 0); - r->ofcall.count = n; - respond(r, nil); - return; - case FsFTindex: - buf = (char *)view_index(f->content.view); - n = strlen(buf); - write_buf(r, buf, n); - respond(r, nil); - return; - case FsFEvent: - respond_event(r); - return; - } - } - /* This is an assert because it should this should not be called if - * the file is not open for reading. */ - assert(!"Read called on an unreadable file"); -} - -/* This function needs to be seriously cleaned up */ -void -fs_write(P9Req *r) { - FileId *f; - char *errstr = nil; - uint i; - - if(r->ifcall.count == 0) { - respond(r, nil); - return; - } - f = r->fid->aux; - - if(!verify_file(f)) { - respond(r, Enofile); - return; - } - - switch(f->tab.type) { - case FsFColRules: - case FsFTagRules: - write_to_buf(r, &f->content.rule->string, &f->content.rule->size, 0); - respond(r, nil); - return; - case FsFKeys: - write_to_buf(r, &def.keys, &def.keyssz, 0); - respond(r, nil); - return; - case FsFCtags: - data_to_cstring(r); - apply_tags(f->content.client, r->ifcall.data); - r->ofcall.count = r->ifcall.count; - respond(r, nil); - return; - case FsFBar: - /* XXX: This should validate after each write */ - i = strlen(f->content.bar->buf); - write_to_buf(r, &f->content.bar->buf, &i, 279); - r->ofcall.count = i - r->ifcall.offset; - respond(r, nil); - return; - case FsFCctl: - data_to_cstring(r); - if((errstr = message_client(f->content.client, r->ifcall.data))) { - respond(r, errstr); - return; - } - r->ofcall.count = r->ifcall.count; - respond(r, nil); - return; - case FsFTctl: - data_to_cstring(r); - if((errstr = message_view(f->content.view, r->ifcall.data))) { - respond(r, errstr); - return; - } - r->ofcall.count = r->ifcall.count; - respond(r, nil); - return; - case FsFRctl: - data_to_cstring(r); - { uint n; - char *toks[32]; - n = tokenize(toks, 32, r->ifcall.data, '\n'); - for(i = 0; i < n; i++) { - if(errstr) - message_root(toks[i]); - else - errstr = message_root(toks[i]); - } - } - if(screen->sel) - focus_view(screen, screen->sel); - if(errstr) { - respond(r, errstr); - return; - } - r->ofcall.count = r->ifcall.count; - respond(r, nil); - return; - case FsFEvent: - if(r->ifcall.data[r->ifcall.count-1] == '\n') - write_event("%.*s", r->ifcall.count, r->ifcall.data); - else - write_event("%.*s\n", r->ifcall.count, r->ifcall.data); - r->ofcall.count = r->ifcall.count; - respond(r, nil); - return; - } - /* This is an assert because this function should not be called if - * the file is not open for writing. */ - assert(!"Write called on an unwritable file"); -} - -void -fs_open(P9Req *r) { - FidLink *fl; - FileId *f = r->fid->aux; - - if(!verify_file(f)) { - respond(r, Enofile); - return; - } - - switch(f->tab.type) { - case FsFEvent: - fl = emallocz(sizeof(FidLink)); - fl->fid = r->fid; - fl->next = pending_event_fids; - pending_event_fids = fl; - break; - } - if((r->ifcall.mode&3) == P9OEXEC) { - respond(r, Enoperm); - return; - } - if((r->ifcall.mode&3) != P9OREAD && !(f->tab.perm & 0200)) { - respond(r, Enoperm); - return; - } - if((r->ifcall.mode&3) != P9OWRITE && !(f->tab.perm & 0400)) { - respond(r, Enoperm); - return; - } - if((r->ifcall.mode&~(3|P9OAPPEND|P9OTRUNC))) { - respond(r, Enoperm); - return; - } - respond(r, nil); -} - -void -fs_create(P9Req *r) { - FileId *f = r->fid->aux; - - switch(f->tab.type) { - default: - /* XXX: This should be taken care of by the library */ - respond(r, Enoperm); - return; - case FsDBars: - if(!strlen(r->ifcall.name)) { - respond(r, Ebadvalue); - return; - } - create_bar(f->content.bar_p, r->ifcall.name); - f = lookup_file(f, r->ifcall.name); - if(!f) { - respond(r, Enofile); - return; - } - r->ofcall.qid.type = f->tab.qtype; - r->ofcall.qid.path = QID(f->tab.type, f->id); - f->next = r->fid->aux; - r->fid->aux = f; - respond(r, nil); - break; - } -} - -void -fs_remove(P9Req *r) { - FileId *f = r->fid->aux; - - if(!verify_file(f)) { - respond(r, Enofile); - return; - } - - - switch(f->tab.type) { - default: - /* XXX: This should be taken care of by the library */ - respond(r, Enoperm); - return; - case FsFBar: - destroy_bar(f->next->content.bar_p, f->content.bar); - draw_bar(screen); - respond(r, nil); - break; - } -} - -void -fs_clunk(P9Req *r) { - Client *c; - FidLink **fl, *ft; - char *buf; - int i; - FileId *f = r->fid->aux; - - if(!verify_file(f)) { - respond(r, nil); - return; - } - - switch(f->tab.type) { - case FsFColRules: - update_rules(&f->content.rule->rule, f->content.rule->string); - break; - case FsFTagRules: - update_rules(&f->content.rule->rule, f->content.rule->string); - for(c=client; c; c=c->next) - apply_rules(c); - update_views(); - break; - case FsFKeys: - update_keys(); - break; - case FsFBar: - buf = f->content.bar->buf; - i = strlen(f->content.bar->buf); - parse_colors(&buf, &i, &f->content.bar->brush.color); - while(i > 0 && buf[i - 1] == '\n') - buf[--i] = '\0'; - strncpy(f->content.bar->text, buf, sizeof(f->content.bar->text)); - draw_bar(screen); - break; - case FsFEvent: - for(fl=&pending_event_fids; *fl; fl=&(*fl)->next) - if((*fl)->fid == r->fid) { - ft = *fl; - *fl = (*fl)->next; - f = ft->fid->aux; - free(f->content.buf); - free(ft); - break; - } - break; - } - respond(r, nil); -} - -void -fs_flush(P9Req *r) { - P9Req **i, **j; - - for(i=&pending_event_reads; i != &outgoing_event_reads; i=&outgoing_event_reads) - for(j=i; *j; j=(P9Req **)&(*j)->aux) - if(*j == r->oldreq) { - *j = (*j)->aux; - respond(r->oldreq, Einterrupted); - goto done; - } -done: - respond(r, nil); -} - -void -fs_freefid(Fid *f) { - FileId *id, *tid; - - for(id=f->aux; id; id = tid) { - tid = id->next; - free_file(id); - } -} diff --git a/geom.c b/geom.c @@ -1,84 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * See LICENSE file for license details. - */ -#include "wmii.h" - -Bool -ptinrect(int x, int y, XRectangle * r) { - return (x >= r->x) && (x < r_east(r)) - && (y >= r->y) && (y < r_south(r)); -} - -BlitzAlign -quadrant(XRectangle *rect, int x, int y) { - BlitzAlign ret = 0; - x -= rect->x; - y -= rect->y; - - if(x >= rect->width * .5) - ret |= EAST; - if(x <= rect->width * .5) - ret |= WEST; - if(y <= rect->height * .5) - ret |= NORTH; - if(y >= rect->height * .5) - ret |= SOUTH; - - return ret; -} - -Cursor -cursor_of_quad(BlitzAlign align) { - switch(align) { - case NEAST: - return cursor[CurNECorner]; - case NWEST: - return cursor[CurNWCorner]; - case SEAST: - return cursor[CurSECorner]; - case SWEST: - return cursor[CurSWCorner]; - default: - return cursor[CurMove]; - } -} - -/* Syntax: <x> <y> <width> <height> */ -int -strtorect(XRectangle *r, const char *val) { - XRectangle new; - if (!val) - return -1; - - if(sscanf(val, "%hd %hd %hu %hu", &new.x, &new.y, &new.width, &new.height) != 4) - return -1; - - *r = new; - return 0; -} - -int -r_east(XRectangle *r) { - return r->x + r->width; -} - -int -r_south(XRectangle *r) { - return r->y + r->height; -} - -BlitzAlign -get_sticky(XRectangle *src, XRectangle *dst) { - BlitzAlign stickycorner = 0; - - if(src->x != dst->x && r_east(src) == r_east(dst)) - stickycorner |= EAST; - else - stickycorner |= WEST; - if(src->y != dst->y && r_south(src) == r_south(dst)) - stickycorner |= SOUTH; - else - stickycorner |= NORTH; - - return stickycorner; -} diff --git a/img/wmii.eps b/img/wmii.eps @@ -0,0 +1,29 @@ +%!PS-Adobe-2.0 EPSF-1.2 +%%BoundingBox: -1 0 51 27 +%%Creator: MetaPost +%%CreationDate: 2007.02.27:1944 +%%Pages: 1 +%%EndProlog +%%Page: 1 1 + 0 6.23616 dtransform truncate idtransform setlinewidth pop [] 0 setdash + 0 setlinecap 2 setlinejoin 10 setmiterlimit +newpath 2.83461 17.00761 moveto +2.83461 2.83461 lineto +14.17302 2.83461 lineto +14.17302 17.00761 lineto +14.17302 2.83461 lineto +25.51143 2.83461 lineto +25.51143 14.173 lineto +36.84984 14.173 lineto stroke + 0 setlinejoin +newpath 36.84984 14.173 moveto +36.84984 0 lineto +36.84984 14.173 lineto +48.18825 14.173 lineto +48.18825 0 lineto stroke +newpath 36.84984 20.40916 moveto +36.84984 26.07837 lineto stroke +newpath 48.18825 20.40916 moveto +48.18825 26.07837 lineto stroke +showpage +%%EOF diff --git a/img/wmii.mp b/img/wmii.mp @@ -0,0 +1,19 @@ +beginfig(1) +%u=0.6cm; +u=0.2cm; +h=3u; +space=u; +linecap:=butt; +linejoin:=beveled; +pickup pencircle scaled 1.1u; +draw (.5u,h)--(.5u,.5u)--(1.5u+space,.5u)--(1.5u+space,h)--(1.5u+space,.5u)--(2.5u+2space,.5u)\ + --(2.5u+2space,h-.5u)--(3.5u+3space,h-.5u); +linejoin:=mitered; +draw (3.5u+3space,h-.5u)--(3.5u+3space,0)--(3.5u+3space,h-.5u)--(4.5u+4space,h-.5u)--(4.5u+4space,0);; + +gap=.6u; +draw (3.5u+3space,h+gap)--(3.5u+3space,h+u+gap); +draw (4.5u+4space,h+gap)--(4.5u+4space,h+u+gap); +endfig + +end diff --git a/include/Makefile b/include/Makefile @@ -0,0 +1,6 @@ +ROOT= .. +include ${ROOT}/mk/hdr.mk + +HFILES = ixp.h ixp_fcall.h + +include ${ROOT}/mk/common.mk diff --git a/include/util.h b/include/util.h @@ -0,0 +1,36 @@ +#define strlcat wmii_strlcat +/* util.c */ +uint tokenize(char *res[], uint reslen, char *str, char delim); +char *estrdup(const char *str); +void *erealloc(void *ptr, uint size); +void *emallocz(uint size); +void *emalloc(uint size); +void fatal(const char *fmt, ...); +int max(int a, int b); +int min(int a, int b); +char *str_nil(char *s); +uint strlcat(char *dst, const char *src, unsigned int siz); + +char *argv0; +#undef ARGBEGIN +#undef ARGEND +#undef ARGF +#undef EARGF +#define ARGBEGIN int _argi, _argtmp, _inargv=0; char *_argv; \ + if(!argv0)argv0=ARGF(); _inargv=1; \ + while(argc && argv[0][0] == '-') { \ + _argi=1; _argv=*argv++; argc++; \ + while(_argv[_argi]) switch(_argv[_argi++]) +#define ARGEND }_inargv=0;USED(_argtmp);USED(_argv);USED(_argi) +#define ARGF() ((_inargv && _argv[_argi]) ? \ + (_argtmp=_argi, _argi=strlen(_argv), _argv+_argtmp) \ + : ((argc > 0) ? (argc--, *argv++) : ((char*)0))) +#define EARGF(f) ((_inargv && _argv[_argi]) ? \ + (_argtmp=_argi, _argi=strlen(_argv), _argv+_argtmp) \ + : ((argc > 0) ? (argc--, *argv++) : ((f), (char*)0))) + +#undef USED +#undef SET +#define USED(x) if(x){}else +#define SET(x) ((x)=0) + diff --git a/key.c b/key.c @@ -1,243 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * See LICENSE file for license details. - */ -#include <string.h> -#include <stdlib.h> -#include <X11/keysym.h> -#include "wmii.h" - -void -init_lock_keys() { - XModifierKeymap *modmap; - KeyCode num_lock; - static int masks[] = { - ShiftMask, LockMask, ControlMask, Mod1Mask, - Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask - }; - int i; - - num_lock_mask = 0; - modmap = XGetModifierMapping(blz.dpy); - num_lock = XKeysymToKeycode(blz.dpy, XStringToKeysym("Num_Lock")); - if(modmap && modmap->max_keypermod > 0) { - int max = (sizeof(masks) / sizeof(int)) * modmap->max_keypermod; - for(i = 0; i < max; i++) - if(num_lock && (modmap->modifiermap[i] == num_lock)) - num_lock_mask = masks[i / modmap->max_keypermod]; - } - XFreeModifiermap(modmap); - valid_mask = 255 & ~(num_lock_mask | LockMask); -} - -ulong -mod_key_of_str(char *val) { - ulong mod = 0; - - if (strstr(val, "Shift")) - mod |= ShiftMask; - if (strstr(val, "Control")) - mod |= ControlMask; - if (strstr(val, "Mod1")) - mod |= Mod1Mask; - if (strstr(val, "Mod2")) - mod |= Mod2Mask; - if (strstr(val, "Mod3")) - mod |= Mod3Mask; - if (strstr(val, "Mod4")) - mod |= Mod4Mask; - if (strstr(val, "Mod5")) - mod |= Mod5Mask; - return mod; -} - -static void -grab_key(Key *k) { - XGrabKey(blz.dpy, k->key, k->mod, blz.root, - True, GrabModeAsync, GrabModeAsync); - if(num_lock_mask) { - XGrabKey(blz.dpy, k->key, k->mod | num_lock_mask, blz.root, - True, GrabModeAsync, GrabModeAsync); - XGrabKey(blz.dpy, k->key, k->mod | num_lock_mask | LockMask, blz.root, - True, GrabModeAsync, GrabModeAsync); - } - XSync(blz.dpy, False); -} - -static void -ungrab_key(Key *k) { - XUngrabKey(blz.dpy, k->key, k->mod, blz.root); - if(num_lock_mask) { - XUngrabKey(blz.dpy, k->key, k->mod | num_lock_mask, blz.root); - XUngrabKey(blz.dpy, k->key, k->mod | num_lock_mask | LockMask, blz.root); - } - XSync(blz.dpy, False); -} - -static Key * -name2key(const char *name) { - Key *k; - for(k=key; k; k=k->lnext) - if(!strncmp(k->name, name, sizeof(k->name))) break; - return k; -} - -static Key * -get_key(const char *name) { - char buf[128]; - char *seq[8]; - char *kstr; - uint i, toks; - static ushort id = 1; - Key *k = 0, *r = 0; - - if((k = name2key(name))) { - ungrab_key(k); - return k; - } - strncpy(buf, name, sizeof(buf)); - toks = tokenize(seq, 8, buf, ','); - for(i = 0; i < toks; i++) { - if(!k) - r = k = emallocz(sizeof(Key)); - else { - k->next = emallocz(sizeof(Key)); - k = k->next; - } - strncpy(k->name, name, sizeof(k->name)); - kstr = strrchr(seq[i], '-'); - if(kstr) - kstr++; - else - kstr = seq[i]; - k->key = XKeysymToKeycode(blz.dpy, XStringToKeysym(kstr)); - k->mod = mod_key_of_str(seq[i]); - } - if(r) { - r->id = id++; - r->lnext = key; - key = r; - } - - return r; -} - -static void -next_keystroke(ulong *mod, KeyCode *code) { - XEvent e; - KeySym sym; - *mod = 0; - - do { - XMaskEvent(blz.dpy, KeyPressMask, &e); - *mod |= e.xkey.state & valid_mask; - *code = (KeyCode) e.xkey.keycode; - sym = XKeycodeToKeysym(blz.dpy, e.xkey.keycode, 0); - } while(IsModifierKey(sym)); -} - -static void -emulate_key_press(ulong mod, KeyCode key) { - XEvent e; - Window client_win; - int revert; - - XGetInputFocus(blz.dpy, &client_win, &revert); - e.xkey.type = KeyPress; - e.xkey.time = CurrentTime; - e.xkey.window = client_win; - e.xkey.display = blz.dpy; - e.xkey.state = mod; - e.xkey.keycode = key; - XSendEvent(blz.dpy, client_win, True, KeyPressMask, &e); - e.xkey.type = KeyRelease; - XSendEvent(blz.dpy, client_win, True, KeyReleaseMask, &e); - XSync(blz.dpy, False); -} - -static Key * -match_keys(Key *k, ulong mod, KeyCode keycode, Bool seq) { - Key *ret = nil, *next; - - for(next = k->tnext; k; (k=next) && (next=k->tnext)) { - if(seq) - k = k->next; - if(k && (k->mod == mod) && (k->key == keycode)) { - k->tnext = ret; - ret = k; - } - } - return ret; -} - -static void -kpress_seq(Window w, Key *done) { - ulong mod; - KeyCode key; - Key *found; - - next_keystroke(&mod, &key); - found = match_keys(done, mod, key, True); - if((done->mod == mod) && (done->key == key)) - emulate_key_press(mod, key); /* double key */ - else { - if(!found) { - XBell(blz.dpy, 0); - } /* grabbed but not found */ - else if(!found->tnext && !found->next) - write_event("Key %s\n", found->name); - else - kpress_seq(w, found); - } -} - -void -kpress(Window w, ulong mod, KeyCode keycode) { - Key *k, *found; - - for(k=key; k; k=k->lnext) - k->tnext=k->lnext; - found = match_keys(key, mod, keycode, False); - if(!found) { - XBell(blz.dpy, 0); - } /* grabbed but not found */ - else if(!found->tnext && !found->next) - write_event("Key %s\n", found->name); - else { - XGrabKeyboard(blz.dpy, w, True, GrabModeAsync, GrabModeAsync, CurrentTime); - kpress_seq(w, found); - XUngrabKeyboard(blz.dpy, CurrentTime); - XSync(blz.dpy, False); - } -} - -void -update_keys() { - Key *k, *n; - char *l, *p; - - init_lock_keys(); - while((k = key)) { - key = key->lnext; - ungrab_key(k); - while((n = k)) { - k = k->next; - free(n); - } - } - for(l = p = def.keys; p && *p;) { - if(*p == '\n') { - *p = 0; - if((k = get_key(l))) - grab_key(k); - *p = '\n'; - l = ++p; - } - else - p++; - } - if(l < p && strlen(l)) { - if((k = get_key(l))) - grab_key(k); - } - XSync(blz.dpy, False); -} diff --git a/main.c b/main.c @@ -1,596 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <X11/Xatom.h> -#include <X11/Xproto.h> -#include <X11/cursorfont.h> -#include <errno.h> -#include <locale.h> -#include <pwd.h> -#include <signal.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <unistd.h> -#include "wmii.h" - -static const char - version[] = "wmii - " VERSION ", ©2007 Kris Maglione\n"; - -static int (*x_error_handler) (Display *, XErrorEvent *); -static char *address, *ns_path; -static Bool check_other_wm; -static struct sigaction sa; -static struct passwd *passwd; -static int sleeperfd, sock; - -static void -usage() { - fatal("usage: wmii [-a <address>] [-r <wmiirc>] [-v]\n"); -} - -static void -scan_wins() { - int i; - uint num; - Window *wins; - XWindowAttributes wa; - Window d1, d2; - - if(XQueryTree(blz.dpy, blz.root, &d1, &d2, &wins, &num)) { - for(i = 0; i < num; i++) { - if(!XGetWindowAttributes(blz.dpy, wins[i], &wa)) - continue; - if(wa.override_redirect || XGetTransientForHint(blz.dpy, wins[i], &d1)) - continue; - if(wa.map_state == IsViewable) - manage_client(create_client(wins[i], &wa)); - } - for(i = 0; i < num; i++) { - if(!XGetWindowAttributes(blz.dpy, wins[i], &wa)) - continue; - if(XGetTransientForHint(blz.dpy, wins[i], &d1) - && wa.map_state == IsViewable) - manage_client(create_client(wins[i], &wa)); - } - } - if(wins) - XFree(wins); -} - -int -win_proto(Window w) { - Atom *protocols; - Atom real; - ulong nitems, extra; - int i, format, status, protos; - - status = XGetWindowProperty( - /* display */ blz.dpy, - /* window */ w, - /* property */ atom[WMProtocols], - /* offset */ 0L, - /* length */ 20L, - /* delete */ False, - /* req_type */ XA_ATOM, - /* type_ret */ &real, - /* format_ret */&format, - /* nitems_ret */&nitems, - /* extra_bytes */&extra, - /* prop_return */(uchar**)&protocols - ); - - if(status != Success || protocols == 0) { - return 0; - } - - if(nitems == 0) { - free(protocols); - return 0; - } - - protos = 0; - for(i = 0; i < nitems; i++) { - if(protocols[i] == atom[WMDelete]) - protos |= WM_PROTOCOL_DELWIN; - } - - free(protocols); - return protos; -} - -static void -ns_display() { - char *p, *disp; - - disp = getenv("DISPLAY"); - if(disp == nil) - fatal("DISPLAY is unset"); - - disp = estrdup(disp); - p = &disp[strlen(disp) - 2]; - if(strcmp(p, ".0") == 0) - *p = '\0'; - - ns_path = emalloc(strlen(disp) + strlen(user) + strlen("/tmp/ns..") + 1); - sprintf(ns_path, "/tmp/ns.%s.%s", user, disp); - - free(disp); -} - -static void -rmkdir(char *path, int mode) { - char *p, *q; - int ret; - char c; - - q = path + strlen(ns_path); - for(p = &path[1]; p <= q; p++) { - c = *p; - if((c == '/') || (c == '\0')) { - *p = '\0'; - ret = mkdir(path, mode); - if((ret == -1) && (errno != EEXIST)) - fatal("Can't create ns_path '%s':", path); - *p = c; - } - } -} - -static void -init_ns() { - struct stat st; - char *p; - - if(address && strncmp(address, "unix!", 5) == 0) { - ns_path = estrdup(&address[5]); - p = strrchr(ns_path, '/'); - if(p != nil) - p = '\0'; - } - else if((p = getenv("WMII_NS_PATH")) || (p = getenv("NAMESPACE"))) - ns_path = p; - else - ns_display(); - - if((ns_path[0] != '/') || (strlen(ns_path) == 0)) - fatal("Bad ns_path"); - - rmkdir(ns_path, 0700); - - if(stat(ns_path, &st)) - fatal("Can't stat ns_path '%s':", ns_path); - if(getuid() != st.st_uid) - fatal("ns_path '%s' exists but is not owned by you", - ns_path); - if(st.st_mode & 077) - fatal("ns_path '%s' exists, but has group or world permissions", - ns_path); -} - -static void -init_environment() { - if(address == nil) - address = getenv("WMII_ADDRESS"); - - init_ns(); - - if(address == nil) { - address = emalloc(strlen(ns_path) + strlen("unix!/wmii") + 1); - sprintf(address, "unix!%s/wmii", ns_path); - } - - setenv("WMII_NS_DIR", ns_path, True); - setenv("WMII_ADDRESS", address, True); -} - -static void -intern_atom(int ident, char *name) { - atom[ident] = XInternAtom(blz.dpy, name, False); -} - -static void -init_atoms() { - intern_atom(WMState, "WM_STATE"); - intern_atom(WMProtocols, "WM_PROTOCOLS"); - intern_atom(WMDelete, "WM_DELETE_WINDOW"); - intern_atom(NetSupported, "_NET_SUPPORTED"); - intern_atom(NetWMName, "_NET_WM_NAME"); - intern_atom(TagsAtom, "_WIN_TAGS"); - - XChangeProperty(blz.dpy, blz.root, atom[NetSupported], XA_ATOM, 32, - PropModeReplace, (uchar *)&atom[NetSupported], 2); -} - -static void -create_cursor(int ident, uint shape) { - cursor[ident] = XCreateFontCursor(blz.dpy, shape); -} - -static void -init_cursors() { - Pixmap pix; - XColor black, dummy; - - create_cursor(CurNormal, XC_left_ptr); - create_cursor(CurNECorner, XC_top_right_corner); - create_cursor(CurNWCorner, XC_top_left_corner); - create_cursor(CurSECorner, XC_bottom_right_corner); - create_cursor(CurSWCorner, XC_bottom_left_corner); - create_cursor(CurMove, XC_fleur); - create_cursor(CurInput, XC_xterm); - - XAllocNamedColor(blz.dpy, DefaultColormap(blz.dpy, blz.screen), - "black", &black, - &dummy); - pix = XCreateBitmapFromData(blz.dpy, blz.root, - (char[]){0}, 1, 1); - - cursor[CurInvisible] = XCreatePixmapCursor(blz.dpy, - pix, pix, - &black, &black, - 0, 0); - - XFreePixmap(blz.dpy, pix); -} - -static void -init_screen(WMScreen *screen) { - Window w; - int ret; - unsigned mask; - XGCValues gcv; - - gcv.subwindow_mode = IncludeInferiors; - gcv.function = GXxor; - gcv.foreground = def.normcolor.bg; - gcv.plane_mask = AllPlanes; - gcv.graphics_exposures = False; - xorgc = XCreateGC(blz.dpy, blz.root, - GCForeground - | GCGraphicsExposures - | GCFunction - | GCSubwindowMode - | GCPlaneMask, - &gcv); - - screen->rect.x = screen->rect.y = 0; - screen->rect.width = DisplayWidth(blz.dpy, blz.screen); - screen->rect.height = DisplayHeight(blz.dpy, blz.screen); - def.snap = screen->rect.height / 63; - - sel_screen = XQueryPointer(blz.dpy, blz.root, - &w, &w, - &ret, &ret, &ret, &ret, - &mask); -} - -/* - * There's no way to check accesses to destroyed windows, thus - * those cases are ignored (especially on UnmapNotify's). - * Other types of errors call Xlib's default error handler, which - * calls exit(). - */ -int -wmii_error_handler(Display *dpy, XErrorEvent *error) { - if(check_other_wm) - fatal("another window manager is already running"); - - if(error->error_code == BadWindow - ||(error->request_code == X_SetInputFocus - && error->error_code == BadMatch) - ||(error->request_code == X_PolyText8 - && error->error_code == BadDrawable) - ||(error->request_code == X_PolyFillRectangle - && error->error_code == BadDrawable) - ||(error->request_code == X_PolySegment - && error->error_code == BadDrawable) - ||(error->request_code == X_ConfigureWindow - && error->error_code == BadMatch) - ||(error->request_code == X_GrabKey - && error->error_code == BadAccess)) - return 0; - - fprintf(stderr, "wmii: fatal error: Xrequest code=%d, Xerror code=%d\n", - error->request_code, error->error_code); - return x_error_handler(blz.dpy, error); /* calls exit() */ -} - -static void -cleanup() { - Client *c; - - for(c=client; c; c=c->next) - reparent_client(c, blz.root, c->sel->rect.x, c->sel->rect.y); - XSync(blz.dpy, False); -} - -static void -cleanup_handler(int signal) { - sa.sa_handler = SIG_DFL; - sigaction(signal, &sa, nil); - - switch(signal) { - case SIGINT: - srv.running = False; - break; - default: - cleanup(); - XCloseDisplay(blz.dpy); - raise(signal); - break; - } -} - -static void -init_traps() { - char buf[1]; - int fd[2]; - - if(pipe(fd) != 0) - fatal("Can't pipe():"); - - switch(fork()) { - case -1: - fatal("Can't fork():"); - break; /* not reached */ - case 0: - close(fd[1]); - close(ConnectionNumber(blz.dpy)); - setsid(); - - blz.dpy = XOpenDisplay(0); - if(!blz.dpy) - fatal("Can't open display"); - - /* Wait for parent to exit */ - read(fd[0], buf, 1); - - XSetInputFocus(blz.dpy, PointerRoot, RevertToPointerRoot, CurrentTime); - XCloseDisplay(blz.dpy); - exit(0); - default: - break; - } - - close(fd[0]); - sleeperfd = fd[1]; - - sa.sa_flags = 0; - sa.sa_handler = cleanup_handler; - sigaction(SIGINT, &sa, nil); - sigaction(SIGTERM, &sa, nil); - sigaction(SIGQUIT, &sa, nil); - sigaction(SIGHUP, &sa, nil); -} - -static void -spawn_command(const char *cmd) { - char *shell, *p; - pid_t pid; - int status; - - /* Double fork hack */ - switch(pid = fork()) { - case -1: - fatal("Can't fork:"); - break; /* Not reached */ - case 0: - switch(fork()) { - case -1: - fatal("Can't fork:"); - break; /* Not reached */ - case 0: - if(setsid() == -1) - fatal("Can't setsid:"); - close(sock); - close(ConnectionNumber(blz.dpy)); - - shell = passwd->pw_shell; - if(shell[0] != '/') - fatal("Shell is not an absolute path: %s", shell); - - /* Run through the user's shell as a login shell */ - p = malloc((strlen(shell) + 2)); - sprintf(p, "-%s", strrchr(shell, '/') + 1); - - execl(shell, p, "-c", cmd, nil); - fatal("Can't exec '%s':", cmd); - break; /* Not reached */ - default: - exit(0); - break; /* Not reached */ - } - default: - waitpid(pid, &status, 0); - if(status != 0) - exit(1); /* Error already printed */ - break; - } -} - -void -check_9pcon(IXPConn *c) { - serve_9pcon(c); - check_x_event(nil); -} - -int -main(int argc, char *argv[]) { - char *wmiirc, *errstr; - WMScreen *s; - XSetWindowAttributes wa; - int i; - - passwd = getpwuid(getuid()); - user = estrdup(passwd->pw_name); - wmiirc = "wmiistartrc"; - - /* command line args */ - for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) { - switch (argv[i][1]) { - case 'v': - printf("%s", version); - exit(0); - break; - case 'V': - verbose = True; - break; - case 'a': - if(argv[i][2] != '\0') - address = &argv[i][2]; - else if(++i < argc) - address = argv[i]; - else - usage(); - break; - case 'r': - if(argv[i][2] != '\0') - wmiirc = &argv[i][2]; - else if(++i < argc) - wmiirc = argv[i]; - else - usage(); - break; - default: - usage(); - break; - } - } - - setlocale(LC_CTYPE, ""); - starting = True; - - blz.dpy = XOpenDisplay(0); - if(!blz.dpy) - fatal("Can't open display"); - blz.screen = DefaultScreen(blz.dpy); - blz.root = RootWindow(blz.dpy, blz.screen); - - check_other_wm = True; - x_error_handler = XSetErrorHandler(wmii_error_handler); - XSelectInput(blz.dpy, blz.root, SubstructureRedirectMask | EnterWindowMask); - XSync(blz.dpy, False); - check_other_wm = False; - - init_environment(); - init_traps(); - - errstr = nil; - sock = ixp_create_sock(address, &errstr); - if(sock < 0) - fatal("Can't create socket '%s': %s", address, errstr); - - if(wmiirc) - spawn_command(wmiirc); - - ixp_server_open_conn(&srv, sock, &p9srv, check_9pcon, nil); - ixp_server_open_conn(&srv, ConnectionNumber(blz.dpy), nil, check_x_event, nil); - - view = nil; - client = nil; - key = nil; - - def.font.fontstr = estrdup(BLITZ_FONT); - def.border = 1; - def.colmode = Coldefault; - - def.mod = Mod1Mask; - strncpy(def.grabmod, "Mod1", sizeof(def.grabmod)); - - strncpy(def.focuscolor.colstr, BLITZ_FOCUSCOLORS, sizeof(def.focuscolor.colstr)); - strncpy(def.normcolor.colstr, BLITZ_NORMCOLORS, sizeof(def.normcolor.colstr)); - loadcolor(&blz, &def.focuscolor); - loadcolor(&blz, &def.normcolor); - - init_atoms(); - init_cursors(); - loadfont(&blz, &def.font); - init_lock_keys(); - - num_screens = 1; - screens = emallocz(num_screens * sizeof(*screens)); - for(i = 0; i < num_screens; i++) { - s = &screens[i]; - init_screen(s); - pmap = XCreatePixmap( - /* display */ blz.dpy, - /* drawable */ blz.root, - /* width */ s->rect.width, - /* height */ s->rect.height, - /* depth */ DefaultDepth(blz.dpy, blz.screen) - ); - wa.event_mask = - SubstructureRedirectMask - | EnterWindowMask - | LeaveWindowMask - | FocusChangeMask; - wa.cursor = cursor[CurNormal]; - XChangeWindowAttributes(blz.dpy, blz.root, CWEventMask | CWCursor, &wa); - wa.override_redirect = 1; - wa.background_pixmap = ParentRelative; - wa.event_mask = - ExposureMask - | ButtonReleaseMask - | FocusChangeMask - | SubstructureRedirectMask - | SubstructureNotifyMask; - s->brect = s->rect; - s->brect.height = labelh(&def.font); - s->brect.y = s->rect.height - s->brect.height; - s->barwin = XCreateWindow( - /* display */ blz.dpy, - /* parent */ RootWindow(blz.dpy, blz.screen), - /* x */ s->brect.x, - /* y */ s->brect.y, - /* width */ s->brect.width, - /* height */ s->brect.height, - /*border_width*/0, - /* depth */ DefaultDepth(blz.dpy, blz.screen), - /* class */ CopyFromParent, - /* visual */ DefaultVisual(blz.dpy, blz.screen), - /* valuemask */ CWOverrideRedirect | CWBackPixmap | CWEventMask, - /* attrubutes */&wa - ); - XSync(blz.dpy, False); - s->bbrush.blitz = &blz; - s->bbrush.gc = XCreateGC(blz.dpy, s->barwin, 0, 0); - s->bbrush.drawable = pmap; - s->bbrush.rect = s->brect; - s->bbrush.rect.x = 0; - s->bbrush.rect.y = 0; - s->bbrush.color = def.normcolor; - s->bbrush.font = &def.font; - s->bbrush.border = 1; - draw_bar(s); - XMapRaised(blz.dpy, s->barwin); - } - - screen = &screens[0]; - screen->focus = nil; - XSetInputFocus(blz.dpy, screen->barwin, RevertToParent, CurrentTime); - - scan_wins(); - starting = False; - update_views(); - if(view) - write_event("FocusTag %s\n", screen->sel->name); - - check_x_event(nil); - errstr = ixp_server_loop(&srv); - if(errstr) - - cleanup(); - XCloseDisplay(blz.dpy); - ixp_server_close(&srv); - close(sleeperfd); - - if(execstr) - execl("/bin/sh", "sh", "-c", execstr, nil); - - if(errstr) - fatal("%s", errstr); - return 0; -} diff --git a/man/Makefile b/man/Makefile @@ -0,0 +1,14 @@ +ROOT=.. +include ${ROOT}/mk/hdr.mk + +TARG = wmii.1 \ + wmiiloop.1 \ + wmiir.1 \ + wmiiwm.1 + +FILTER = sed "s|CONFPREFIX|${CONFPREFIX}|g; \ + s|CONFVERSION|${CONFVERSION}|g; \ + s|P9PATHS|${P9PATHS}|g; \ + s|AWKPATH|${AWKPATH}|g" + +include ${ROOT}/mk/man.mk diff --git a/man/wmii.1 b/man/wmii.1 @@ -0,0 +1,205 @@ +.TH WMII 1 wmii-VERSION +.SH NAME +wmii \(em window manager improved\(emimproved +.SH SYNOPSIS +.B wmii +.SH DESCRIPTION +.SS Overview +.B wmii +is a dynamic window manager for X11. See +.BR wmiiwm (1) +for more info. +.SS Actions +An action is a shell script in the default setup, but it can actually be +any executable file. It is executed usually by selecting it from the +actions menu. +You can customize an action by copying it from the global action +directory CONFPREFIX/wmii-3.5 to $HOME/.wmii-3.5 and then editing the copy to +fit your needs. Of course you can also create your own actions there; make +sure that they are executable. +.P +Here is a list of the default actions: +.TP 2 +quit +leave the window manager nicely +.TP 2 +status +periodically print date and load average to the bar +.TP 2 +welcome +display a welcome message that contains the wmii tutorial +.TP 2 +wmiirc +configure wmii +.SS Default Key Bindings +.PD 0 +.B Moving Around +.RS 2 +.TP 16 +.I Key +.I Action +.TP +.B Mod-h +Move to a +.B window +to the +.B left +of the one currently focused +.TP +.B Mod-l +Move to a +.B window +to the +.B right +of the one currently focused +.TP +.B Mod-j +Move to a +.B window below +the one currently focused +.TP +.B Mod-k +Move to a +.B window above +the one currently focused +.TP +.B Mod-space +.B Toggle +between the managed and floating +.B layer +.TP +.BI Mod-t \ tag +Move to the +.B view +of the given +.I tag +.TP +.B Mod-[0-9] +Move to the +.B view +with the given number +.PD 1 +.P +.RE +.B Moving Things Around +.RS 2 +.PD 0 +.TP 16 +.I Key +.I Action +.TP +.B Mod-Shift-h +Move the current window +.B window +to a column on the +.B left +.TP +.B Mod-Shift-l +Move the current window +.B window +to a column on the +.B right +.TP +.B Mod-Shift-j +Move the current +.B window below +the one beneath it +.TP +.B Mod-Shift-k +Move the current +.B window above +the one above it +.TP +.B Mod-Shift-space +.B Toggle +the current +.B window +between the managed and floating +.B layer +.TP +.BI Mod-Shift-t \ tag +Move the current window to the +.B view +of the given +.I tag +.TP +.B Mod-Shift-[0-9] +Move to the current window to the +.B view +with the given number +.PD 1 +.P +.RE +.B Miscellaneous +.RS 2 +.PD 0 +.TP 16 +.I Key +.I Action +.TP +.B Mod-m +Switch the current column to +.B max mode +.TP +.B Mod-s +Switch the current column to +.B stack mode +.TP +.B Mod-d +Switch the current column to +.B default mode +.TP +.B Mod-Shift-c +.B Kill +the selected client +.TP +.BI Mod-p \ program +.B Execute +.I program +.TP +.BI Mod-a \ action +Execute the +.B named action +.TP +.B Mod-Enter +Start an +.B xterm + +.SS Configuration +If you feel the need to change the default configuration, then customize (as +described above) the +.B wmiirc +action. This action is executed at the end of the +.B wmii +script and does all the work of setting up the window manager, the key +bindings, the bar labels, etc. +.SH FILES +.TP +/tmp/ns.$USER.${DISPLAY%.0}/wmii +The wmii socket file which provides a 9P service. +.TP +CONFPREFIX/wmii-3.5 +Global action directory. +.TP +$HOME/.wmii-3.5 +User-specific action directory. Actions are first searched here. +.SH ENVIRONMENT +.TP +HOME, DISPLAY +See the section +.B FILES +above. +.P +The following variables are set and exported within +.B wmii +and thus can be used in actions: +.TP +WMII_ADDRESS +Socket file of +.BR wmiiwm (1). +Used by +.BR wmiir (1). +.SH SEE ALSO +.BR wmiiwm (1), +.BR dmenu (1), +.BR wmiir (1) diff --git a/man/wmiiloop.1 b/man/wmiiloop.1 @@ -0,0 +1,79 @@ +.TH WMIIR 1 wmii\-VERSION +.SH NAME +wmiiloop \(em wmii event loop processor +.SH SYNOPSIS +.B cat +.I events +| +.B wmiiloop +| +.B sh +.br +.SH DESCRIPTION +.SS Overview +.B wmiiloop +is an awk script which preprocesses +.BR sh (1) +syntax to simplify writing a wmii event loop. + +.br +.B eventloop +sends any text up to the first line containing +.I # +.IR Events|Actions|Key , +and any text after any other line begining with +.IR # , +directly to its standard output. Any other line is processed based +on its first word, with every indented line after it taken as its +.IR body . +.TP 2 +.BI "Key " sequence +The key sequence +.I sequence +is bound to its +.IR body . +.I sequence +is written to +.I /keys +and +.I body +is executed, with +.I $key +set to +.I sequence +whenever the key is pressed. + +.TP 2 +.BI "Event " name +Whenever an event with +.I name +is read from +.IR /event , +.I body +is executed, with +.I $event +set to +.IR name , +and +.I $[1\-9] +set to further arguments. + +.TP 2 +.BI "Action " name +A function called +.BI Action_ name +is created with +.I body +as its body, and +.I name +is added to the +.I $ACTIONS +variable. + +.SH SEE ALSO +.BR wmii (1), +.BR wmiir (1), +.BR wmiiwm (1) +.B PREFIX/etc/wmiirc + +http://www.cs.bell\-labs.com/sys/man/5/INDEX.html diff --git a/man/wmiir.1 b/man/wmiir.1 @@ -0,0 +1,51 @@ +.TH WMIIR 1 wmii-VERSION +.SH NAME +wmiir \(em wrapper script for ixpc +.SH SYNOPSIS +.B wmiir +.RB [ \-a +.IR address ] +.I action +.I file +.br +.B wmiir +.B \-v +.SH DESCRIPTION +.SS Overview +.B wmiir +is a wrapper script for +.BR ixpc (1) +to conveniently access the filesystem of +.BR wmiiwm (1) +from the command line or from shell +scripts. It can be used to configure +.BR wmii (1). + +.SS Options +See the manual page of +.BR ixpc (1) . +.SH ENVIRONMENT +.TP +WMII_ADDRESS +The address of the wmiiwm file server to which wmiir will connect. +.SH EXAMPLES +.TP +.B wmiir ls / +This prints the root directory of the wmii filesystem. For more information +about the contents of this filesystem, see +.BR wmiiwm (1). +.TP +.B echo -n quit | wmiir write /ctl +Write 'quit' to the main control file of the wmii filesystem, effectively +leaving wmii. +.TP +.B echo -n view 2 | wmiir write /ctl +Bring into view all clients tagged '2'. To learn about clients and +tags, see +.BR wmii (1). +.SH SEE ALSO +.BR wmii (1), +.BR ixpc (1), +.BR wmiiwm (1) + +http://www.cs.bell-labs.com/sys/man/5/INDEX.html diff --git a/man/wmiiwm.1 b/man/wmiiwm.1 @@ -0,0 +1,150 @@ +.TH WMIIWM 1 wmii-VERSION +.SH NAME +wmiiwm \(em window manager improved\(emimproved (core) +.SH SYNOPSIS +.B wmiiwm +.B \-a +.I <address> +.RB [ \-c ] +.RB [ \-v ] +.SH DESCRIPTION + +.PD 0 +.SS Overview +.BR wmiiwm (1) +is the core of window manager improved\(emimproved. +.P +.B wmii +is a dynamic window manager for X11. In contrast to static window management +the user rarely has to think about how to organize windows, no matter what he +is doing or how many applications are used at the same time. The window manager +adapts to the current environment and fits to the needs of the user, rather +than forcing him to use a preset, fixed layout and trying to shoehorn all +windows and applications into it. +.P +.B wmii +supports classic and tiled window management with extended keyboard and mouse +control. The classic window management arranges windows in a floating layer +in which windows can be moved and resized freely. The tiled window management +is based on columns which split up the screen horizontally. Each column handles +arbitrary windows and arranges them vertically in a non\-overlapping way. They +can then be moved and resized between and within columns at will. +.P +.B wmii +provides a virtual filesystem which represents the internal state similar to +the procfs of Unix operating systems. Modifying this virtual filesystem results +in changing the state of the window manager. The virtual filesystem service can +be accessed through 9P\-capable client programs, like +.BR wmiir (1) . +This allows simple and powerful remote control of the core window manager. +.P +.B wmii +basically consists of clients, columns, views, and the bar, which are described +in detail in the +.B Terminology +section. +.SS Options +.TP +.BI \-a " address" +Lets you specify the address which +.B wmiiwm +uses to listen for connections. The syntax for +.I address +is taken (along with many other profound ideas) from the Plan 9 operating +system and has the form +.B unix!/path/to/socket +for unix socket files, and +.B tcp!hostname!port +for tcp sockets. +.TP +.B \-c +Checks if another window manager is running. If not it exits with termination code +0. +.TP +.B \-v +Prints version information to stdout, then exits. +.SS Terminology +.TP 2 +Display +A running X server instance consisting of input devices and screens. +.TP 2 +Screen +A physical or virtual (Xinerama or +.BR Xnest (1)) +screen of an X display. A screen displays a bar window and a view at a time. +.TP 2 +Window +A (rectangular) drawable X object which is displayed on a screen, usually an +application window. +.TP 2 +Client +An application window surrounded by a frame window containing a border and a +title\-bar. +.TP 2 +Floating layer +A screen layer of +.B wmii +on top of all other layers, where clients are arranged in a classic (floating) +way. They can be resized or moved freely. +.TP 2 +Managed layer +A screen layer of +.B wmii +behind the floating layer, where clients are arranged in a non\-overlapping +(managed) way. Here, the window manager dynamically assigns each client a +size and position. The managed layer consists of columns. +.TP 2 +Tag +Alphanumeric strings which can be assigned to a client. This provides a +mechanism to group clients with similar properties. Clients can have one +tag, e.g. +.IR work , +or several tags, e.g. +.IR work+mail . +Tags are separated with the +.I + +character. +.TP 2 +View +A set of clients containing a specific tag, quite similiar to a workspace in +other window managers. It consists of the floating and managed layers. +.TP 2 +Column +A column is a screen area which arranges clients vertically in a +non\-overlapping way. Columns provide three different modes, which arrange +clients with equal size, stacked, or maximized respectively. Clients can be +moved and resized between and within columns freely. +.TP 2 +Bar +The bar at the bottom of the screen displays a label for each view and +allows the creation of arbitrary user\-defined labels. +.TP 2 +Event +An event is a message which can be read from a special file in the filesystem +of +.BR wmiiwm , +such as a mouse button press, a key press, or a message written by a different +9P\-client. +.SS Basic window management +Running a raw +.B wmiiwm +process without the +.BR wmii (1) +script provides basic window management capabilities already. However to use +it effectively, remote control through its filesystem interface is necessary. +By default it is only usable with the mouse in conjunction with the +.I Mod1 (Alt) +modifier key. Other interactions like customizing the style, killing or +retagging clients, or grabbing keys cannot be achieved without accessing the +filesystem. +.P +The filesystem can be accessed by connecting to the +.I address +of +.B wmiiwm +with any 9P\-capable client, like +.BR wmiir (1). +.SH SEE ALSO +.BR wmii (1), +.BR dmenu (1), +.BR wmiir (1) diff --git a/mk/common.mk b/mk/common.mk @@ -0,0 +1,18 @@ +all: + +install: all +depend: cleandep + +MANDIRS=${MAN}/man1 +mkdirs: + for i in ${BIN} ${ETC} ${MANDIRS} ${DIRS}; do \ + test -d $$i || echo MKDIR $$i; \ + mkdir -pm 0755 $$i; \ + done + +install: ${HFILES:.h=.install} + +cleandep: + rm .depend 2>/dev/null || true + +.PHONY: all options clean dist install uninstall depend cleandep diff --git a/mk/dir.mk b/mk/dir.mk @@ -0,0 +1,29 @@ +dall: + for i in ${DIRS}; do \ + if [ ! -d $$i ]; then echo Skipping nonexistent directory: $$i 1>&2; continue; fi; \ + echo MAKE all ${BASE}$$i/; \ + (cd $$i && ${MAKE} BASE="${BASE}$$i/" all) || exit $?; \ + done +dclean: + for i in ${DIRS}; do \ + if [ ! -d $$i ]; then echo Skipping nonexistent directory: $$i 1>&2; continue; fi; \ + echo MAKE clean ${BASE}$$i/; \ + (cd $$i && ${MAKE} BASE="${BASE}$$i/" clean) || exit $?; \ + done +dinstall: + for i in ${DIRS}; do \ + if [ ! -d $$i ]; then echo Skipping nonexistent directory: $$i 1>&2; continue; fi; \ + echo MAKE install ${BASE}$$i/; \ + (cd $$i && ${MAKE} BASE="${BASE}$$i/" install) || exit $?; \ + done +ddepend: + for i in ${DIRS}; do \ + if [ ! -d $$i ]; then echo Skipping nonexistent directory: $$i 1>&2; continue; fi; \ + echo MAKE depend ${BASE}$$i/; \ + (cd $$i && ${MAKE} BASE="${BASE}$$i/" depend) || exit $?; \ + done + +all: dall +clean: dclean +install: dinstall +depend: ddepend diff --git a/mk/hdr.mk b/mk/hdr.mk @@ -0,0 +1,60 @@ +.SILENT: +.SUFFIXES: .O .o .c .sh .rc .awk .1 .depend .install .clean +all: + +.c.o: + ${COMPILE} $@ $< + +.c.depend: + ${DEPEND} $< >>.depend + +.o.O: + ${LINK} $@ $< + +.awk.O: + echo FILTER ${BASE}$< + ${FILTER} $< >$@ + +.rc.O: + echo FILTER ${BASE}$< + ${FILTER} $< >$@ + +.sh.O: + echo FILTER ${BASE}$< + ${FILTER} $< >$@ + +.O.install: + echo INSTALL $* + cp -f $< ${BIN}/$* + chmod 0755 ${BIN}/$* + +.a.install: + echo INSTALL $< + cp -f $< ${LIBDIR}/$< + chmod 0644 ${LIBDIR}/$< + +.h.install: + echo INSTALL $< + cp -f $< ${INCLUDE}/$< + chmod 0644 ${INCLUDE}/$< + +.1.install: + echo INSTALL man $*'(1)' + ${FILTER} $< >${MAN}/man1/$< + chmod 0644 ${MAN}/man1/$< + +.O.clean: + rm $< || true 2>/dev/null + rm $*.o || true 2>/dev/null +.o.clean: + rm $< || true 2>/dev/null + +printinstall: +mkdirs: +clean: +install: printinstall mkdirs + +COMPILE= CC="${CC}" CFLAGS="${CFLAGS} ${EXCFLAGS}" ${ROOT}/util/compile +LINK= LD="${LD}" LDFLAGS="${LDFLAGS} ${EXLDFLAGS}" ${ROOT}/util/link + +include ${ROOT}/config.mk diff --git a/mk/lib.mk b/mk/lib.mk @@ -0,0 +1,24 @@ +LIB = ${TARG}.a +OFILES = ${OBJ:=.o} + +all: ${HFILES} ${LIB} + +install: ${TARG}.install +clean: libclean +depend: ${OBJ:=.depend} + +libclean: + for i in ${LIB} ${OFILES}; do \ + rm $$i; \ + done 2>/dev/null || true + +printinstall: + echo 'Install directories:' + echo ' Lib: ${LIBDIR}' + +${LIB}: ${OFILES} + @echo AR $@ + @${AR} $@ ${OFILES} + @${RANLIB} $@ + +include ${ROOT}/mk/common.mk diff --git a/mk/man.mk b/mk/man.mk @@ -0,0 +1,7 @@ +install: ${TARG:.1=.install} + +printinstall: + echo 'Install directories:' + echo ' Man: ${MAN}' + +include ${ROOT}/mk/common.mk diff --git a/mk/many.mk b/mk/many.mk @@ -0,0 +1,18 @@ +PROGS = ${TARG:=.O} + +all: ${PROGS} + +install: ${TARG:=.install} +clean: manyclean + +printinstall: + echo 'Install directories:' + echo ' Bin: ${BIN}' + +manyclean: + for i in ${TARG}; do \ + rm $$i.o; rm $$i.O; \ + done 2>/dev/null || true + +include ${ROOT}/mk/common.mk + diff --git a/mk/one.mk b/mk/one.mk @@ -0,0 +1,22 @@ +PROG = ${TARG}.O +OFILES = ${OBJ:=.o} + +all: ${PROG} + +install: ${TARG}.install +clean: oneclean +depend: ${OBJ:=.depend} + +printinstall: + echo 'Install directories:' + echo ' Bin: ${BIN}' + +oneclean: + for i in ${PROG} ${OFILES}; do \ + rm $$i; \ + done 2>/dev/null || true + +${PROG}: ${OFILES} ${LIB} + ${LINK} $@ ${OFILES} ${LIB} + +include ${ROOT}/mk/common.mk diff --git a/mk/wmii.mk b/mk/wmii.mk @@ -0,0 +1,2 @@ +VERSION = $$(hg tip --template 'hg{rev}' 2>/dev/null) +CONFVERSION = 3.5 diff --git a/mouse.c b/mouse.c @@ -1,490 +0,0 @@ -/* Copyright ©2006 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <stdlib.h> -#include <string.h> -#include "wmii.h" - -enum { - ButtonMask = - ButtonPressMask | ButtonReleaseMask, - MouseMask = - ButtonMask | PointerMotionMask -}; - -static void -rect_morph_xy(XRectangle *rect, int dx, int dy, BlitzAlign *mask) { - BlitzAlign new_mask = 0; - if(*mask & NORTH) { - if(rect->height - dy >= 0 || *mask & SOUTH) { - rect->y += dy; - rect->height -= dy; - } - else { - rect->y += rect->height; - rect->height = dy - rect->height; - new_mask ^= NORTH|SOUTH; - } - } - if(*mask & SOUTH) { - if(rect->height + dy >= 0 || *mask & NORTH) - rect->height += dy; - else { - rect->height = -dy - rect->height; - rect->y -= rect->height; - new_mask ^= NORTH|SOUTH; - } - } - if(*mask & EAST) { - if(rect->width + dx >= 0 || *mask & WEST) - rect->width += dx; - else { - rect->width = -dx - rect->width; - rect->x -= rect->width; - new_mask ^= EAST|WEST; - } - } - if(*mask & WEST) { - if(rect->width - dx >= 0 || *mask & EAST) { - rect->x += dx; - rect->width -= dx; - } - else { - rect->x += rect->width; - rect->width = dx - rect->width; - new_mask ^= EAST|WEST; - } - } - *mask ^= new_mask; -} - -typedef struct { - XRectangle *rects; - int num; - int x1, y1, x2, y2; - BlitzAlign mask; - int *delta; -} SnapArgs; - -static void -snap_line(SnapArgs *a) { - int i, t_xy; - - /* horizontal */ - if(a->y1 == a->y2 && (a->mask & (NORTH|SOUTH))) { - for(i=0; i < a->num; i++) { - if(!(r_east(&a->rects[i]) < a->x1) || - (a->rects[i].x > a->x2)) { - - if(abs(a->rects[i].y - a->y1) <= abs(*a->delta)) - *a->delta = a->rects[i].y - a->y1; - - t_xy = r_south(&a->rects[i]); - if(abs(t_xy - a->y1) < abs(*a->delta)) - *a->delta = t_xy - a->y1; - } - } - } - else if (a->mask & (EAST|WEST)) { - /* This is the same as above, tr/xy/yx/, - * s/width/height/, s/height/width/ */ - for(i=0; i < a->num; i++) { - if(!(r_south(&a->rects[i]) < a->y1) || - (a->rects[i].y > a->y2)) { - - if(abs(a->rects[i].x - a->x1) <= abs(*a->delta)) - *a->delta = a->rects[i].x - a->x1; - - t_xy = r_east(&a->rects[i]); - if(abs(t_xy - a->x1) < abs(*a->delta)) - *a->delta = t_xy - a->x1; - } - } - } -} - -BlitzAlign -snap_rect(XRectangle *rects, int num, XRectangle *current, BlitzAlign *mask, int snap) { - SnapArgs a = { rects, num, 0, 0, 0, 0, *mask, nil }; - int dx, dy; - BlitzAlign ret; - - dx = dy = snap + 1; - - a.x1 = current->x; - a.x2 = r_east(current); - a.delta = &dy; - if(*mask & NORTH) { - a.y2 = a.y1 = current->y; - snap_line(&a); - } - if(*mask & SOUTH) { - a.y2 = a.y1 = r_south(current); - snap_line(&a); - } - - a.y1 = current->y; - a.y2 = r_south(current); - a.delta = &dx; - if(*mask & EAST) { - a.x1 = a.x2 = r_east(current); - snap_line(&a); - } - if(*mask & WEST) { - a.x1 = a.x2 = current->x; - snap_line(&a); - } - - rect_morph_xy(current, - abs(dx) <= snap ? dx : 0, - abs(dy) <= snap ? dy : 0, - mask); - - ret = *mask; - if(abs(dx) <= snap) - ret ^= EAST|WEST; - if(abs(dy) <= snap) - ret ^= NORTH|SOUTH; - return ret ^ CENTER; -} - -static void -draw_xor_border(XRectangle *r) { - XRectangle xor; - - xor = *r; - xor.x += 2; - xor.y += 2; - xor.width = xor.width > 4 ? xor.width - 4 : 0; - xor.height = xor.height > 4 ? xor.height - 4 : 0; - - XSetLineAttributes(blz.dpy, xorgc, 1, LineSolid, CapNotLast, JoinMiter); - XSetForeground(blz.dpy, xorgc, def.focuscolor.bg); - if(xor.height > 4 && xor.width > 2) - XDrawLine(blz.dpy, blz.root, xorgc, - xor.x + 2, - xor.y + xor.height / 2, - r_east(&xor) - 2, - xor.y + xor.height / 2); - if(xor.width > 4 && xor.height > 2) - XDrawLine(blz.dpy, blz.root, xorgc, - xor.x + xor.width / 2, - xor.y + 2, - xor.x + xor.width / 2, - r_south(&xor) - 2); - XSetLineAttributes(blz.dpy, xorgc, 4, LineSolid, CapNotLast, JoinMiter); - XDrawRectangles(blz.dpy, blz.root, xorgc, &xor, 1); -} - -static void -find_droppoint(Frame *frame, int x, int y, XRectangle *rect, Bool do_move) { - View *v; - Area *a, *a_prev; - Frame *f, *f_close; - - v = frame->view; - rect->y = 0; - rect->height = screen->rect.height - screen->brect.height; - - a_prev = v->area; - for(a = a_prev->next; a && a->next; a = a->next) { - if(x < r_east(&a->rect)) - break; - a_prev = a; - } - if(x < (a->rect.x + labelh(&def.font))) { - rect->x = a->rect.x - 4; - rect->width = 8; - - if(do_move) { - a = new_column(v, a_prev, 0); - send_to_area(a, frame); - focus(frame->client, False); - } - return; - } - if(x > (r_east(&a->rect) - labelh(&def.font))) { - rect->x = r_east(&a->rect) - 4; - rect->width = 8; - - if(do_move) { - a = new_column(v, a, 0); - send_to_area(a, frame); - focus(frame->client, False); - } - return; - } - - rect->x = a->rect.x; - rect->width = a->rect.width; - - f_close = nil; - for(f = a->frame; f; f = f->anext) { - if(y < f->rect.y) - break; - if(y < r_south(&f->rect)) - break; - f_close = f; - } - if(f == nil) - f = f_close; - if(y < (f->rect.y + labelh(&def.font))) { - rect->y = f->rect.y; - rect->height = 2; - if(f_close) { - rect->y = r_south(&f_close->rect); - rect->height = f->rect.y - rect->y; - } - if(do_move) - goto do_move; - return; - } - if(y > r_south(&f->rect) - labelh(&def.font)) { - rect->y = r_south(&f->rect); - rect->height = (screen->rect.height - labelh(&def.font) - rect->y); - if(f->anext) - rect->height = (f->anext->rect.y - rect->y); - if(do_move) - goto do_move; - return; - } - - *rect = f->rect; - if(do_move) { - swap_frames(frame, f); - focus(frame->client, False); - } - return; - -do_move: - if(frame == f) - return; - if(a != frame->area) - send_to_area(a, frame); - remove_frame(frame); - insert_frame(f, frame, False); - arrange_column(f->area, False); - focus(frame->client, True); -} - -static void -do_managed_move(Client *c) { - XRectangle frect, ofrect; - Window dummy; - XEvent ev; - Frame *f; - uint di; - int x, y, i; - - focus(c, False); - f = c->sel; - - XSync(blz.dpy, False); - if(XGrabPointer(blz.dpy, c->framewin, False, MouseMask, GrabModeAsync, GrabModeAsync, - None, cursor[CurMove], CurrentTime) != GrabSuccess) - return; - XGrabServer(blz.dpy); - - XQueryPointer(blz.dpy, blz.root, &dummy, &dummy, &i, &i, &x, &y, &di); - - find_droppoint(f, x, y, &frect, False); - draw_xor_border(&frect); - for(;;) { - XMaskEvent(blz.dpy, MouseMask | ExposureMask, &ev); - switch (ev.type) { - case ButtonRelease: - draw_xor_border(&frect); - - find_droppoint(f, x, y, &frect, True); - - XUngrabServer(blz.dpy); - XUngrabPointer(blz.dpy, CurrentTime); - XSync(blz.dpy, False); - return; - case MotionNotify: - ofrect = frect; - x = ev.xmotion.x_root; - y = ev.xmotion.y_root; - - find_droppoint(f, x, y, &frect, False); - - if(memcmp(&frect, &ofrect, sizeof(frect))) { - draw_xor_border(&ofrect); - draw_xor_border(&frect); - } - break; - case Expose: - (handler[Expose])(&ev); - break; - default: break; - } - } -} - -void -do_mouse_resize(Client *c, Bool grabbox, BlitzAlign align) { - BlitzAlign grav; - Window dummy; - Cursor cur; - XEvent ev; - XRectangle *rects, ofrect, frect, origin; - int snap, dx, dy, pt_x, pt_y, hr_x, hr_y, i; - uint num, di; - Bool floating; - float rx, ry; - Frame *f; - - f = c->sel; - floating = f->area->floating; - origin = frect = f->rect; - cur = cursor_of_quad(align); - if(floating) { - rects = rects_of_view(f->area->view, &num, (grabbox ? c->frame : nil)); - snap = screen->rect.height / 66; - }else{ - rects = nil; - snap = 0; - } - - if(align == CENTER) { - if(!grabbox) - cur = cursor[CurInvisible]; - if(!floating) { - do_managed_move(c); - return; - } - } - - XQueryPointer(blz.dpy, c->framewin, &dummy, &dummy, &i, &i, &pt_x, &pt_y, &di); - rx = (float)pt_x / frect.width; - ry = (float)pt_y / frect.height; - - if(XGrabPointer( - /* display */ blz.dpy, - /* window */ c->framewin, - /* owner_events */ False, - /* event_mask */ MouseMask, - /* pointer_mode */ GrabModeAsync, - /* keyboard_mode */ GrabModeAsync, - /* confine_to */ None, - /* cursor */ cur, - /* time */ CurrentTime - ) != GrabSuccess) - return; - - XQueryPointer(blz.dpy, blz.root, &dummy, &dummy, &i, &i, &pt_x, &pt_y, &di); - - if(align != CENTER) { - hr_x = dx = frect.width / 2; - hr_y = dy = frect.height / 2; - if(align&NORTH) dy -= hr_y; - if(align&SOUTH) dy += hr_y; - if(align&EAST) dx += hr_x; - if(align&WEST) dx -= hr_x; - XTranslateCoordinates(blz.dpy, c->framewin, blz.root, dx, dy, - &pt_x, &pt_y, &dummy); - XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, pt_x, pt_y); - } - else if(!grabbox) { - hr_x = screen->rect.width / 2; - hr_y = screen->rect.height / 2; - XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, hr_x, hr_y); - flushevents(PointerMotionMask, False); - } - - - XSync(blz.dpy, False); - if(!grabbox) { - XGrabServer(blz.dpy); - draw_xor_border(&frect); - }else - unmap_client(c, IconicState); - - for(;;) { - XMaskEvent(blz.dpy, MouseMask | ExposureMask, &ev); - switch (ev.type) { - case ButtonRelease: - if(!grabbox) - draw_xor_border(&frect); - - if(!floating) - resize_column(c, &frect); - else - resize_client(c, &frect); - - if(!grabbox) { - if(align != CENTER) - XTranslateCoordinates(blz.dpy, c->framewin, blz.root, - (frect.width * rx), (frect.height * ry), - &pt_x, &pt_y, &dummy); - if(pt_y > screen->brect.y) - pt_y = screen->brect.y - 1; - XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, pt_x, pt_y); - XUngrabServer(blz.dpy); - }else - map_client(c); - - if(rects) - free(rects); - - XUngrabPointer(blz.dpy, CurrentTime); - XSync(blz.dpy, False); - return; - case MotionNotify: - ofrect = frect; - dx = ev.xmotion.x_root; - dy = ev.xmotion.y_root; - - if(align == CENTER && !grabbox) { - if(dx == hr_x && dy == hr_y) - continue; - XWarpPointer(blz.dpy, None, blz.root, 0, 0, 0, 0, hr_x, hr_y); - dx -= hr_x; - dy -= hr_y; - }else{ - dx -= pt_x; - dy -= pt_y; - } - pt_x += dx; - pt_y += dy; - - rect_morph_xy(&origin, dx, dy, &align); - if(align != CENTER) - check_frame_constraints(&origin); - frect = origin; - - if(floating) - grav = snap_rect(rects, num, &frect, &align, snap); - else - grav = align ^ CENTER; - - apply_sizehints(c, &frect, floating, True, grav); - - if(grabbox) { - XMoveWindow(blz.dpy, c->framewin, frect.x, frect.y); - XSync(blz.dpy, False); - } else { - draw_xor_border(&ofrect); - draw_xor_border(&frect); - } - break; - case Expose: - (handler[Expose])(&ev); - break; - default: - break; - } - } -} - -void -grab_button(Window w, uint button, ulong mod) { - XGrabButton(blz.dpy, button, mod, w, False, ButtonMask, - GrabModeSync, GrabModeSync, None, None); - if((mod != AnyModifier) && num_lock_mask) { - XGrabButton(blz.dpy, button, mod | num_lock_mask, w, False, ButtonMask, - GrabModeSync, GrabModeAsync, None, None); - XGrabButton(blz.dpy, button, mod | num_lock_mask | LockMask, w, False, - ButtonMask, GrabModeSync, GrabModeAsync, None, None); - } -} diff --git a/printevent.c b/printevent.c @@ -1,997 +0,0 @@ -/* - * Original code posted to comp.sources.x - * Modifications by Russ Cox <rsc@swtch.com>. - */ - -/* -Path: uunet!wyse!mikew -From: mikew@wyse.wyse.com (Mike Wexler) -Newsgroups: comp.sources.x -Subject: v02i056: subroutine to print events in human readable form, Part01/01 -Message-ID: <1935@wyse.wyse.com> -Date: 22 Dec 88 19:28:25 GMT -Organization: Wyse Technology, San Jose -Lines: 1093 -Approved: mikew@wyse.com - -Submitted-by: richsun!darkstar!ken -Posting-number: Volume 2, Issue 56 -Archive-name: showevent/part01 - - -There are times during debugging when it would be real useful to be able to -print the fields of an event in a human readable form. Too many times I found -myself scrounging around in section 8 of the Xlib manual looking for the valid -fields for the events I wanted to see, then adding printf's to display the -numeric values of the fields, and then scanning through X.h trying to decode -the cryptic detail and state fields. After playing with xev, I decided to -write a couple of standard functions that I could keep in a library and call -on whenever I needed a little debugging verbosity. The first function, -GetType(), is useful for returning the string representation of the type of -an event. The second function, ShowEvent(), is used to display all the fields -of an event in a readable format. The functions are not complicated, in fact, -they are mind-numbingly boring - but that's just the point nobody wants to -spend the time writing functions like this, they just want to have them when -they need them. - -A simple, sample program is included which does little else but to demonstrate -the use of these two functions. These functions have saved me many an hour -during debugging and I hope you find some benefit to these. If you have any -comments, suggestions, improvements, or if you find any blithering errors you -can get it touch with me at the following location: - - ken@richsun.UUCP -*/ - -#include <stdio.h> -#include <X11/Intrinsic.h> -#include <X11/Xproto.h> -#include "wmii.h" -#include "printevent.h" - -static char* sep = " "; - -/******************************************************************************/ -/**** Miscellaneous routines to convert values to their string equivalents ****/ -/******************************************************************************/ - -/* Returns the string equivalent of a boolean parameter */ -static char* -TorF(int bool) -{ - switch (bool) { - case True: - return ("True"); - - case False: - return ("False"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a property notify state */ -static char* -PropertyState(int state) -{ - switch (state) { - case PropertyNewValue: - return ("PropertyNewValue"); - - case PropertyDelete: - return ("PropertyDelete"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a visibility notify state */ -static char* -VisibilityState(int state) -{ - switch (state) { - case VisibilityUnobscured: - return ("VisibilityUnobscured"); - - case VisibilityPartiallyObscured: - return ("VisibilityPartiallyObscured"); - - case VisibilityFullyObscured: - return ("VisibilityFullyObscured"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a timestamp */ -static char* -ServerTime(Time time) -{ - ulong msec; - ulong sec; - ulong min; - ulong hr; - ulong day; - static char buffer[32]; - - msec = time % 1000; - time /= 1000; - sec = time % 60; - time /= 60; - min = time % 60; - time /= 60; - hr = time % 24; - time /= 24; - day = time; - -if(0) - sprintf(buffer, "%lu day%s %02lu:%02lu:%02lu.%03lu", - day, day == 1 ? "" : "(s)", hr, min, sec, msec); - - sprintf(buffer, "%lud%luh%lum%lu.%03lds", day, hr, min, sec, msec); - return (buffer); -} - -/* Simple structure to ease the interpretation of masks */ -typedef struct MaskType MaskType; -struct MaskType -{ - uint value; - char *string; -}; - -/* Returns the string equivalent of a mask of buttons and/or modifier keys */ -static char* -ButtonAndOrModifierState(uint state) -{ - static char buffer[256]; - static MaskType masks[] = { - {Button1Mask, "Button1Mask"}, - {Button2Mask, "Button2Mask"}, - {Button3Mask, "Button3Mask"}, - {Button4Mask, "Button4Mask"}, - {Button5Mask, "Button5Mask"}, - {ShiftMask, "ShiftMask"}, - {LockMask, "LockMask"}, - {ControlMask, "ControlMask"}, - {Mod1Mask, "Mod1Mask"}, - {Mod2Mask, "Mod2Mask"}, - {Mod3Mask, "Mod3Mask"}, - {Mod4Mask, "Mod4Mask"}, - {Mod5Mask, "Mod5Mask"}, - }; - int num_masks = sizeof(masks) / sizeof(MaskType); - int i; - Boolean first = True; - - buffer[0] = 0; - - for (i = 0; i < num_masks; i++) - if (state & masks[i].value) { - if (first) { - first = False; - strcpy(buffer, masks[i].string); - } else { - strcat(buffer, " | "); - strcat(buffer, masks[i].string); - } - } - return (buffer); -} - -/* Returns the string equivalent of a mask of configure window values */ -static char* -ConfigureValueMask(uint valuemask) -{ - static char buffer[256]; - static MaskType masks[] = { - {CWX, "CWX"}, - {CWY, "CWY"}, - {CWWidth, "CWWidth"}, - {CWHeight, "CWHeight"}, - {CWBorderWidth, "CWBorderWidth"}, - {CWSibling, "CWSibling"}, - {CWStackMode, "CWStackMode"}, - }; - int num_masks = sizeof(masks) / sizeof(MaskType); - int i; - Boolean first = True; - - buffer[0] = 0; - - for (i = 0; i < num_masks; i++) - if (valuemask & masks[i].value) { - if (first) { - first = False; - strcpy(buffer, masks[i].string); - } else { - strcat(buffer, " | "); - strcat(buffer, masks[i].string); - } - } - - return (buffer); -} - -/* Returns the string equivalent of a motion hint */ -static char* -IsHint(char is_hint) -{ - switch (is_hint) { - case NotifyNormal: - return ("NotifyNormal"); - - case NotifyHint: - return ("NotifyHint"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of an id or the value "None" */ -static char* -MaybeNone(int value) -{ - static char buffer[16]; - - if (value == None) - return ("None"); - else { - sprintf(buffer, "0x%x", value); - return (buffer); - } -} - -/* Returns the string equivalent of a colormap state */ -static char* -ColormapState(int state) -{ - switch (state) { - case ColormapInstalled: - return ("ColormapInstalled"); - - case ColormapUninstalled: - return ("ColormapUninstalled"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a crossing detail */ -static char* -CrossingDetail(int detail) -{ - switch (detail) { - case NotifyAncestor: - return ("NotifyAncestor"); - - case NotifyInferior: - return ("NotifyInferior"); - - case NotifyVirtual: - return ("NotifyVirtual"); - - case NotifyNonlinear: - return ("NotifyNonlinear"); - - case NotifyNonlinearVirtual: - return ("NotifyNonlinearVirtual"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a focus change detail */ -static char* -FocusChangeDetail(int detail) -{ - switch (detail) { - case NotifyAncestor: - return ("NotifyAncestor"); - - case NotifyInferior: - return ("NotifyInferior"); - - case NotifyVirtual: - return ("NotifyVirtual"); - - case NotifyNonlinear: - return ("NotifyNonlinear"); - - case NotifyNonlinearVirtual: - return ("NotifyNonlinearVirtual"); - - case NotifyPointer: - return ("NotifyPointer"); - - case NotifyPointerRoot: - return ("NotifyPointerRoot"); - - case NotifyDetailNone: - return ("NotifyDetailNone"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a configure detail */ -static char* -ConfigureDetail(int detail) -{ - switch (detail) { - case Above: - return ("Above"); - - case Below: - return ("Below"); - - case TopIf: - return ("TopIf"); - - case BottomIf: - return ("BottomIf"); - - case Opposite: - return ("Opposite"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a grab mode */ -static char* -GrabMode(int mode) -{ - switch (mode) { - case NotifyNormal: - return ("NotifyNormal"); - - case NotifyGrab: - return ("NotifyGrab"); - - case NotifyUngrab: - return ("NotifyUngrab"); - - case NotifyWhileGrabbed: - return ("NotifyWhileGrabbed"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a mapping request */ -static char* -MappingRequest(int request) -{ - switch (request) { - case MappingModifier: - return ("MappingModifier"); - - case MappingKeyboard: - return ("MappingKeyboard"); - - case MappingPointer: - return ("MappingPointer"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a stacking order place */ -static char* -Place(int place) -{ - switch (place) { - case PlaceOnTop: - return ("PlaceOnTop"); - - case PlaceOnBottom: - return ("PlaceOnBottom"); - - default: - return ("?"); - } -} - -/* Returns the string equivalent of a major code */ -static char* -MajorCode(int code) -{ - static char buffer[32]; - - switch (code) { - case X_CopyArea: - return ("X_CopyArea"); - - case X_CopyPlane: - return ("X_CopyPlane"); - - default: - sprintf(buffer, "0x%x", code); - return (buffer); - } -} - -/* Returns the string equivalent the keycode contained in the key event */ -static char* -Keycode(XKeyEvent *ev) -{ - static char buffer[256]; - KeySym keysym_str; - char *keysym_name; - char string[256]; - - XLookupString(ev, string, 64, &keysym_str, NULL); - - if (keysym_str == NoSymbol) - keysym_name = "NoSymbol"; - else if (!(keysym_name = XKeysymToString(keysym_str))) - keysym_name = "(no name)"; - sprintf(buffer, "%u (keysym 0x%x \"%s\")", - (int)ev->keycode, (int)keysym_str, keysym_name); - return (buffer); -} - -/* Returns the string equivalent of an atom or "None"*/ -static char* -AtomName(Display *dpy, Atom atom) -{ - static char buffer[256]; - char *atom_name; - - if (atom == None) - return ("None"); - - atom_name = XGetAtomName(dpy, atom); - strncpy(buffer, atom_name, 256); - XFree(atom_name); - return (buffer); -} - -/******************************************************************************/ -/**** Routines to print out readable values for the field of various events ***/ -/******************************************************************************/ - -static void -VerbMotion(XMotionEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); - fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); - fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "x_root=%d y_root=%d%s", ev->x_root, ev->y_root, sep); - fprintf(stderr, "state=%s%s", ButtonAndOrModifierState(ev->state), sep); - fprintf(stderr, "is_hint=%s%s", IsHint(ev->is_hint), sep); - fprintf(stderr, "same_screen=%s\n", TorF(ev->same_screen)); -} - -static void -VerbButton(XButtonEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); - fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); - fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "x_root=%d y_root=%d%s", ev->x_root, ev->y_root, sep); - fprintf(stderr, "state=%s%s", ButtonAndOrModifierState(ev->state), sep); - fprintf(stderr, "button=%s%s", ButtonAndOrModifierState(ev->button), sep); - fprintf(stderr, "same_screen=%s\n", TorF(ev->same_screen)); -} - -static void -VerbColormap(XColormapEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "colormap=%s%s", MaybeNone(ev->colormap), sep); - fprintf(stderr, "new=%s%s", TorF(ev->new), sep); - fprintf(stderr, "state=%s\n", ColormapState(ev->state)); -} - -static void -VerbCrossing(XCrossingEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); - fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); - fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "x_root=%d y_root=%d%s", ev->x_root, ev->y_root, sep); - fprintf(stderr, "mode=%s%s", GrabMode(ev->mode), sep); - fprintf(stderr, "detail=%s%s", CrossingDetail(ev->detail), sep); - fprintf(stderr, "same_screen=%s%s", TorF(ev->same_screen), sep); - fprintf(stderr, "focus=%s%s", TorF(ev->focus), sep); - fprintf(stderr, "state=%s\n", ButtonAndOrModifierState(ev->state)); -} - -static void -VerbExpose(XExposeEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); - fprintf(stderr, "count=%d\n", ev->count); -} - -static void -VerbGraphicsExpose(XGraphicsExposeEvent *ev) -{ - fprintf(stderr, "drawable=0x%x%s", (int)ev->drawable, sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); - fprintf(stderr, "major_code=%s%s", MajorCode(ev->major_code), sep); - fprintf(stderr, "minor_code=%d\n", ev->minor_code); -} - -static void -VerbNoExpose(XNoExposeEvent *ev) -{ - fprintf(stderr, "drawable=0x%x%s", (int)ev->drawable, sep); - fprintf(stderr, "major_code=%s%s", MajorCode(ev->major_code), sep); - fprintf(stderr, "minor_code=%d\n", ev->minor_code); -} - -static void -VerbFocus(XFocusChangeEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "mode=%s%s", GrabMode(ev->mode), sep); - fprintf(stderr, "detail=%s\n", FocusChangeDetail(ev->detail)); -} - -static void -VerbKeymap(XKeymapEvent *ev) -{ - int i; - - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "key_vector="); - for (i = 0; i < 32; i++) - fprintf(stderr, "%02x", ev->key_vector[i]); - fprintf(stderr, "\n"); -} - -static void -VerbKey(XKeyEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); - if(ev->subwindow) - fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); - fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); - fprintf(stderr, "[%d,%d]%s", ev->x, ev->y, sep); - fprintf(stderr, "root=[%d,%d]%s", ev->x_root, ev->y_root, sep); - if(ev->state) - fprintf(stderr, "state=%s%s", ButtonAndOrModifierState(ev->state), sep); - fprintf(stderr, "keycode=%s%s", Keycode(ev), sep); - if(!ev->same_screen) - fprintf(stderr, "!same_screen"); - fprintf(stderr, "\n"); - return; - - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "root=0x%x%s", (int)ev->root, sep); - fprintf(stderr, "subwindow=0x%x%s", (int)ev->subwindow, sep); - fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "x_root=%d y_root=%d%s", ev->x_root, ev->y_root, sep); - fprintf(stderr, "state=%s%s", ButtonAndOrModifierState(ev->state), sep); - fprintf(stderr, "keycode=%s%s", Keycode(ev), sep); - fprintf(stderr, "same_screen=%s\n", TorF(ev->same_screen)); -} - -static void -VerbProperty(XPropertyEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "atom=%s%s", AtomName(ev->display, ev->atom), sep); - fprintf(stderr, "time=%s%s", ServerTime(ev->time), sep); - fprintf(stderr, "state=%s\n", PropertyState(ev->state)); -} - -static void -VerbResizeRequest(XResizeRequestEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "width=%d height=%d\n", ev->width, ev->height); -} - -static void -VerbCirculate(XCirculateEvent *ev) -{ - fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "place=%s\n", Place(ev->place)); -} - -static void -VerbConfigure(XConfigureEvent *ev) -{ - fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); - fprintf(stderr, "border_width=%d%s", ev->border_width, sep); - fprintf(stderr, "above=%s%s", MaybeNone(ev->above), sep); - fprintf(stderr, "override_redirect=%s\n", TorF(ev->override_redirect)); -} - -static void -VerbCreateWindow(XCreateWindowEvent *ev) -{ - fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); - fprintf(stderr, "border_width=%d%s", ev->border_width, sep); - fprintf(stderr, "override_redirect=%s\n", TorF(ev->override_redirect)); -} - -static void -VerbDestroyWindow(XDestroyWindowEvent *ev) -{ - fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); - fprintf(stderr, "window=0x%x\n", (int)ev->window); -} - -static void -VerbGravity(XGravityEvent *ev) -{ - fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "x=%d y=%d\n", ev->x, ev->y); -} - -static void -VerbMap(XMapEvent *ev) -{ - fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "override_redirect=%s\n", TorF(ev->override_redirect)); -} - -static void -VerbReparent(XReparentEvent *ev) -{ - fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "override_redirect=%s\n", TorF(ev->override_redirect)); -} - -static void -VerbUnmap(XUnmapEvent *ev) -{ - fprintf(stderr, "event=0x%x%s", (int)ev->event, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "from_configure=%s\n", TorF(ev->from_configure)); -} - -static void -VerbCirculateRequest(XCirculateRequestEvent *ev) -{ - fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "place=%s\n", Place(ev->place)); -} - -static void -VerbConfigureRequest(XConfigureRequestEvent *ev) -{ - fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "x=%d y=%d%s", ev->x, ev->y, sep); - fprintf(stderr, "width=%d height=%d%s", ev->width, ev->height, sep); - fprintf(stderr, "border_width=%d%s", ev->border_width, sep); - fprintf(stderr, "above=%s%s", MaybeNone(ev->above), sep); - fprintf(stderr, "detail=%s%s", ConfigureDetail(ev->detail), sep); - fprintf(stderr, "value_mask=%s\n", ConfigureValueMask(ev->value_mask)); -} - -static void -VerbMapRequest(XMapRequestEvent *ev) -{ - fprintf(stderr, "parent=0x%x%s", (int)ev->parent, sep); - fprintf(stderr, "window=0x%x\n", (int)ev->window); -} - -static void -VerbClient(XClientMessageEvent *ev) -{ - int i; - - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "message_type=%s%s", AtomName(ev->display, ev->message_type), sep); - fprintf(stderr, "format=%d\n", ev->format); - fprintf(stderr, "data (shown as longs)="); - for (i = 0; i < 5; i++) - fprintf(stderr, " 0x%08lx", ev->data.l[i]); - fprintf(stderr, "\n"); -} - -static void -VerbMapping(XMappingEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "request=%s%s", MappingRequest(ev->request), sep); - fprintf(stderr, "first_keycode=0x%x%s", ev->first_keycode, sep); - fprintf(stderr, "count=0x%x\n", ev->count); -} - -static void -VerbSelectionClear(XSelectionClearEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "selection=%s%s", AtomName(ev->display, ev->selection), sep); - fprintf(stderr, "time=%s\n", ServerTime(ev->time)); -} - -static void -VerbSelection(XSelectionEvent *ev) -{ - fprintf(stderr, "requestor=0x%x%s", (int)ev->requestor, sep); - fprintf(stderr, "selection=%s%s", AtomName(ev->display, ev->selection), sep); - fprintf(stderr, "target=%s%s", AtomName(ev->display, ev->target), sep); - fprintf(stderr, "property=%s%s", AtomName(ev->display, ev->property), sep); - fprintf(stderr, "time=%s\n", ServerTime(ev->time)); -} - -static void -VerbSelectionRequest(XSelectionRequestEvent *ev) -{ - fprintf(stderr, "owner=0x%x%s", (int)ev->owner, sep); - fprintf(stderr, "requestor=0x%x%s", (int)ev->requestor, sep); - fprintf(stderr, "selection=%s%s", AtomName(ev->display, ev->selection), sep); - fprintf(stderr, "target=%s%s", AtomName(ev->display, ev->target), sep); - fprintf(stderr, "property=%s%s", AtomName(ev->display, ev->property), sep); - fprintf(stderr, "time=%s\n", ServerTime(ev->time)); -} - -static void -VerbVisibility(XVisibilityEvent *ev) -{ - fprintf(stderr, "window=0x%x%s", (int)ev->window, sep); - fprintf(stderr, "state=%s\n", VisibilityState(ev->state)); -} - -/******************************************************************************/ -/************ Return the string representation for type of an event ***********/ -/******************************************************************************/ - -char *eventtype(XEvent *ev) -{ - static char buffer[20]; - - switch (ev->type) { - case KeyPress: - return ("KeyPress"); - case KeyRelease: - return ("KeyRelease"); - case ButtonPress: - return ("ButtonPress"); - case ButtonRelease: - return ("ButtonRelease"); - case MotionNotify: - return ("MotionNotify"); - case EnterNotify: - return ("EnterNotify"); - case LeaveNotify: - return ("LeaveNotify"); - case FocusIn: - return ("FocusIn"); - case FocusOut: - return ("FocusOut"); - case KeymapNotify: - return ("KeymapNotify"); - case Expose: - return ("Expose"); - case GraphicsExpose: - return ("GraphicsExpose"); - case NoExpose: - return ("NoExpose"); - case VisibilityNotify: - return ("VisibilityNotify"); - case CreateNotify: - return ("CreateNotify"); - case DestroyNotify: - return ("DestroyNotify"); - case UnmapNotify: - return ("UnmapNotify"); - case MapNotify: - return ("MapNotify"); - case MapRequest: - return ("MapRequest"); - case ReparentNotify: - return ("ReparentNotify"); - case ConfigureNotify: - return ("ConfigureNotify"); - case ConfigureRequest: - return ("ConfigureRequest"); - case GravityNotify: - return ("GravityNotify"); - case ResizeRequest: - return ("ResizeRequest"); - case CirculateNotify: - return ("CirculateNotify"); - case CirculateRequest: - return ("CirculateRequest"); - case PropertyNotify: - return ("PropertyNotify"); - case SelectionClear: - return ("SelectionClear"); - case SelectionRequest: - return ("SelectionRequest"); - case SelectionNotify: - return ("SelectionNotify"); - case ColormapNotify: - return ("ColormapNotify"); - case ClientMessage: - return ("ClientMessage"); - case MappingNotify: - return ("MappingNotify"); - } - sprintf(buffer, "%d", ev->type); - return buffer; -} - -/******************************************************************************/ -/**************** Print the values of all fields for any event ****************/ -/******************************************************************************/ - -void printevent(XEvent *e) -{ - XAnyEvent *ev = (void*)e; - char *name; - - if(ev->window) { - XFetchName(blz.dpy, ev->window, &name); - if(name) { - fprintf(stderr, "\ttitle=%s\n", name); - XFree(name); - } - } - fprintf(stderr, "%3ld %-20s ", ev->serial, eventtype(e)); - if(ev->send_event) - fprintf(stderr, "(sendevent) "); - if(0){ - fprintf(stderr, "type=%s%s", eventtype(e), sep); - fprintf(stderr, "serial=%lu%s", ev->serial, sep); - fprintf(stderr, "send_event=%s%s", TorF(ev->send_event), sep); - fprintf(stderr, "display=0x%p%s", ev->display, sep); - } - - switch (ev->type) { - case MotionNotify: - VerbMotion((void*)ev); - break; - - case ButtonPress: - case ButtonRelease: - VerbButton((void*)ev); - break; - - case ColormapNotify: - VerbColormap((void*)ev); - break; - - case EnterNotify: - case LeaveNotify: - VerbCrossing((void*)ev); - break; - - case Expose: - VerbExpose((void*)ev); - break; - - case GraphicsExpose: - VerbGraphicsExpose((void*)ev); - break; - - case NoExpose: - VerbNoExpose((void*)ev); - break; - - case FocusIn: - case FocusOut: - VerbFocus((void*)ev); - break; - - case KeymapNotify: - VerbKeymap((void*)ev); - break; - - case KeyPress: - case KeyRelease: - VerbKey((void*)ev); - break; - - case PropertyNotify: - VerbProperty((void*)ev); - break; - - case ResizeRequest: - VerbResizeRequest((void*)ev); - break; - - case CirculateNotify: - VerbCirculate((void*)ev); - break; - - case ConfigureNotify: - VerbConfigure((void*)ev); - break; - - case CreateNotify: - VerbCreateWindow((void*)ev); - break; - - case DestroyNotify: - VerbDestroyWindow((void*)ev); - break; - - case GravityNotify: - VerbGravity((void*)ev); - break; - - case MapNotify: - VerbMap((void*)ev); - break; - - case ReparentNotify: - VerbReparent((void*)ev); - break; - - case UnmapNotify: - VerbUnmap((void*)ev); - break; - - case CirculateRequest: - VerbCirculateRequest((void*)ev); - break; - - case ConfigureRequest: - VerbConfigureRequest((void*)ev); - break; - - case MapRequest: - VerbMapRequest((void*)ev); - break; - - case ClientMessage: - VerbClient((void*)ev); - break; - - case MappingNotify: - VerbMapping((void*)ev); - break; - - case SelectionClear: - VerbSelectionClear((void*)ev); - break; - - case SelectionNotify: - VerbSelection((void*)ev); - break; - - case SelectionRequest: - VerbSelectionRequest((void*)ev); - break; - - case VisibilityNotify: - VerbVisibility((void*)ev); - break; - } -} - diff --git a/printevent.h b/printevent.h @@ -1,2 +0,0 @@ -char *eventtype(XEvent*); -void printevent(XEvent*); diff --git a/rule.c b/rule.c @@ -1,86 +0,0 @@ -/* - * Copyright ©2006 Anselm R. Garbe <garbeam at gmail dot com> - * See LICENSE file for license details. - */ - -#include <string.h> -#include <stdlib.h> -#include "wmii.h" - -/* basic rule matching language /regex/ -> value - * regex might contain POSIX regex syntax defined in regex(3) */ -enum { - IGNORE, - REGEX, - VALUE -}; - -void -trim(char *str, const char *chars) { - const char *cp; - char *sp, *sn; - - for(cp = chars; *cp; cp++) { - for(sp = sn = str; *sn; sn++) { - if(*sn != *cp) - *(sp++) = *sn; - } - *sp = 0; - } -} - -void -update_rules(Rule **rule, const char *data) { - int mode = IGNORE; - Rule *rul; - char *p, *r = nil, *v = nil, regex[256], value[256]; - - if(!data || !strlen(data)) - return; - while((rul = *rule)) { - *rule = rul->next; - regfree(&rul->regex); - free(rul); - } - for(p = (char *)data; *p; p++) - switch(mode) { - case IGNORE: - if(*p == '/') { - mode = REGEX; - r = regex; - } - else if(*p == '>') { - mode = VALUE; - value[0] = 0; - v = value; - } - break; - case REGEX: - if(*p == '/') { - mode = IGNORE; - *r = 0; - } - else { - *r = *p; - r++; - } - break; - case VALUE: - if(*p == '\n' || *p == 0) { - *rule = emallocz(sizeof(Rule)); - *v = 0; - trim(value, " \t/"); - if(!regcomp(&(*rule)->regex, regex, 0)) { - strncpy((*rule)->value, value, sizeof(rul->value)); - rule = &(*rule)->next; - } - else free(*rule); - mode = IGNORE; - } - else { - *v = *p; - v++; - } - break; - } -} diff --git a/util.c b/util.c @@ -1,146 +0,0 @@ -/* Written by Kris Maglione <fbsdaemon at gmail dot com> */ -/* Public domain */ -#include <errno.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include "wmii.h" - -void -fatal(const char *fmt, ...) { - va_list ap; - int err; - - err = errno; - fprintf(stderr, "wmii: fatal: "); - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - if(fmt[strlen(fmt)-1] == ':') - fprintf(stderr, " %s\n", strerror(err)); - else - fprintf(stderr, "\n"); - - exit(1); -} - -/* Can't malloc */ -void -mfatal(char *name, uint size) { - const char - couldnot[] = "wmii: fatal: Could not ", - paren[] = "() ", - bytes[] = " bytes\n"; - char sizestr[8]; - int i; - - i = sizeof(sizestr); - do { - sizestr[--i] = '0' + (size%10); - size /= 10; - } while(size > 0); - - write(1, couldnot, sizeof(couldnot)-1); - write(1, name, strlen(name)); - write(1, paren, sizeof(paren)-1); - write(1, sizestr+i, sizeof(sizestr)-i); - write(1, bytes, sizeof(bytes)-1); - - exit(1); -} - -void * -emalloc(uint size) { - void *ret = malloc(size); - if(!ret) - mfatal("malloc", size); - return ret; -} - -void * -emallocz(uint size) { - void *ret = emalloc(size); - memset(ret, 0, size); - return ret; -} - -void * -erealloc(void *ptr, uint size) { - void *ret = realloc(ptr, size); - if(!ret) - mfatal("realloc", size); - return ret; -} - -char * -estrdup(const char *str) { - void *ret = strdup(str); - if(!ret) - mfatal("strdup", strlen(str)); - return ret; -} - -uint -tokenize(char *res[], uint reslen, char *str, char delim) { - char *s; - uint i; - - i = 0; - s = str; - while(i < reslen && *s) { - while(*s == delim) - *(s++) = '\0'; - if(*s) - res[i++] = s; - while(*s && *s != delim) - s++; - } - return i; -} - -int -max(int a, int b) { - if(a > b) - return a; - return b; -} - -int -min(int a, int b) { - if(a < b) - return a; - return b; -} - -char * -str_nil(char *s) { - if(s) - return s; - return "<nil>"; -} - -uint -strlcat(char *dst, const char *src, uint size) { - const char *s; - char *d; - int n, len; - - d = dst; - s = src; - n = size; - while(n-- > 0 && *d != '\0') - d++; - len = n; - - while(*s != '\0') { - if(n-- > 0) - *d++ = *s; - s++; - } - if(len > 0) - *d = '\0'; - return size - n - 1; -} diff --git a/util/compile b/util/compile @@ -0,0 +1,20 @@ +#!/bin/sh -f + +outfile="$1"; shift + +# Derived from Russ Cox's 9c in plan9port. + +xtmp=/tmp/cc.$$.$USER.out + +echo CC ${BASE}$outfile +$CC -o $outfile $CFLAGS $@ 2>$xtmp +status=$? + +cat $xtmp \ +| egrep -v ': error: .Each undeclared identifier|: error: for each function it appears|is dangerous, better use|is almost always misused|: In function |: At top level:|support .long long.|In file included from| from|use of C99 long long|ISO C forbids conversion' \ +| sed 's/ .first use in this function.$//; s/\"\([^\"][^\"]*\)\", line \([0-9][0-9]*\)/\1:\2/g' \ +| uniq 1>&2 + +rm -f $xtmp $xtmp.status +exit $status + diff --git a/util/link b/util/link @@ -0,0 +1,31 @@ +#!/bin/sh -f + +outfile="$1"; shift + +# Derived from Russ Cox's 9l in plan9port. +ofiles="" +args="" +for i +do + case "$i" in + *.[ao]) + ofiles="$ofiles $i" + ;; + *) + args="$args $i" + ;; + esac +done + +xtmp=/tmp/ld.$$.$USER.out + +echo LD ${BASE}$outfile +$LD -o $outfile $ofiles $LDFLAGS $args >$xtmp 2>&1 +status=$? + +sed 's/.*: In function `[^:]*: *//' $xtmp | egrep . | +egrep -v 'is almost always misused|is dangerous, better use' +rm -f $xtmp + +exit $status + diff --git a/view.c b/view.c @@ -1,446 +0,0 @@ -/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * Copyright ©2006-2007 Kris Maglione <fbsdaemon@gmail.com> - * See LICENSE file for license details. - */ -#include <stdlib.h> -#include <string.h> -#include "wmii.h" - -static Bool -is_empty(View *v) { - Area *a; - for(a=v->area; a; a=a->next) - if(a->frame) - return False; - return True; -} - -Frame * -clientframe_of_view(View *v, Client *c) { - Frame *f; - for(f=c->frame; f; f=f->cnext) - if(f->area->view == v) - break; - return f; -} - -static void -assign_sel_view(View *v) { - if(screen->sel != v) { - if(screen->sel) - write_event("UnfocusTag %s\n",screen->sel->name); - screen->sel = v; - write_event("FocusTag %s\n", screen->sel->name); - } -} - -Client * -sel_client_of_view(View *v) { - return v->sel && v->sel->sel ? v->sel->sel->client : nil; -} - -View * -get_view(const char *name) { - View *v; - int cmp; - - for(v = view; v; v=v->next) - if((cmp=strcmp(name, v->name)) >= 0) - break; - if(!v || cmp != 0) - v = create_view(name); - return v; -} - -View * -create_view(const char *name) { - static ushort id = 1; - View **i, *v = emallocz(sizeof(View)); - - v->id = id++; - strncpy(v->name, name, sizeof(v->name)); - create_area(v, nil, 0); - create_area(v, v->area, 0); - for(i=&view; *i; i=&(*i)->next) - if(strcmp((*i)->name, name) < 0) break; - v->next = *i; - *i = v; - write_event("CreateTag %s\n", v->name); - if(!screen->sel) - assign_sel_view(v); - return v; -} - -void -destroy_view(View *v) { - Area *a; - View **i, *tv; - - while((a = v->area)) { - v->area = a->next; - destroy_area(a); - }; - for(i=&view; *i; i=&(*i)->next) - if(*i == v) break; - *i = v->next; - write_event("DestroyTag %s\n", v->name); - if(v == screen->sel) { - for(tv=view; tv && tv->next; tv=tv->next) - if(tv->next == *i) break; - if(tv) - focus_view(screen, tv); - } - free(v); -} - -static void -update_frame_selectors(View *v) { - Area *a; - Frame *f; - - for(a=v->area; a; a=a->next) - for(f=a->frame; f; f=f->anext) - f->client->sel = f; -} - -void -focus_view(WMScreen *s, View *v) { - View *old; - Frame *f; - Client *c; - - old = screen->sel; - XGrabServer(blz.dpy); - assign_sel_view(v); - update_frame_selectors(v); - for(c=client; c; c=c->next) - if((f = c->sel)) { - if(f->view == v) { - resize_client(c, &f->rect); - update_client_grab(c); - } else { - unmap_frame(c); - unmap_client(c, IconicState); - } - } - restack_view(v); - focus_area(v->sel); - draw_frames(); - XSync(blz.dpy, False); - XUngrabServer(blz.dpy); - flushevents(EnterWindowMask, False); -} - -void -select_view(const char *arg) { - char buf[256]; - - strncpy(buf, arg, sizeof(buf)); - trim(buf, " \t+/"); - if(!strlen(buf)) - return; - if(!strncmp(buf, ".", 2) || !strncmp(buf, "..", 3)) - return; - assign_sel_view(get_view(buf)); - update_views(); /* performs focus_view */ -} - -void -attach_to_view(View *v, Frame *f) { - Client *c = f->client; - - c->revert = nil; - if(c->trans || c->floating || c->fixedsize || c->fullscreen) - v->sel = v->area; - else if(starting && v->sel->floating) - v->sel = v->area->next; - attach_to_area(v->sel, f, False); -} - -void -restack_view(View *v) { - Area *a; - Frame *f; - Client *c; - uint n, i; - static Window *wins = nil; - static uint winssz = 0; - - if(v != screen->sel) - return; - - i = 0; - n = 0; - - for(c=client; c; c=c->next) - i++; - if(i == 0) - return; - if(i >= winssz) { - winssz = 2 * i; - wins = erealloc(wins, sizeof(Window) * winssz); - } - - for(f=v->area->stack; f; f=f->snext) - if(f->client->fullscreen) - wins[n++] = f->client->framewin; - wins[n++] = screen->barwin; - for(f=v->area->stack; f; f=f->snext) - if(!f->client->fullscreen) - wins[n++] = f->client->framewin; - for(a=v->area->next; a; a=a->next) { - if(a->frame) { - wins[n++] = a->sel->client->framewin; - for(f=a->frame; f; f=f->anext) - if(f != a->sel) - wins[n++] = f->client->framewin; - } - } - if(n) - XRestackWindows(blz.dpy, wins, n); -} - -void -scale_view(View *v, float w) { - uint xoff, num_col; - uint min_width; - Area *a; - float scale, dx = 0; - int wdiff = 0; - - min_width = screen->rect.width/NCOL; - - if(!v->area->next) - return; - - num_col = 0; - for(a=v->area->next; a; a=a->next) - num_col++, dx += a->rect.width; - - scale = w / dx; - xoff = 0; - for(a=v->area->next; a; a=a->next) { - a->rect.width *= scale; - if(!a->next) - a->rect.width = w - xoff; - xoff += a->rect.width; - } - /* min_width can only be respected when there is enough space; - * the caller should guarantee this */ - if(num_col * min_width > w) - return; - xoff = 0; - for(a=v->area->next, num_col--; a; a=a->next, num_col--) { - if(a->rect.width < min_width) - a->rect.width = min_width; - else if((wdiff = xoff + a->rect.width - w + num_col * min_width) > 0) - a->rect.width -= wdiff; - if(!a->next) - a->rect.width = w - xoff; - xoff += a->rect.width; - } -} - -void -arrange_view(View *v) { - uint xoff = 0; - Area *a; - - if(!v->area->next) - return; - scale_view(v, screen->rect.width); - for(a=v->area->next; a; a=a->next) { - a->rect.x = xoff; - a->rect.y = 0; - a->rect.height = screen->rect.height - screen->brect.height; - xoff += a->rect.width; - arrange_column(a, False); - } -} - -XRectangle * -rects_of_view(View *v, uint *num, Frame *ignore) { - XRectangle *result; - Frame *f; - int i; - - i = 2; - for(f=v->area->frame; f; f=f->anext) - i++; - result = emallocz(i * sizeof(XRectangle)); - i = 0; - for(f=v->area->frame; f; f=f->anext) - if(f != ignore) - result[i++] = f->rect; - result[i++] = screen->rect; - result[i++] = screen->brect; - *num = i; - return result; -} - -/* XXX: This will need cleanup */ -uchar * -view_index(View *v) { - uint a_i, buf_i, n; - int len; - Frame *f; - Area *a; - - len = BUFFER_SIZE; - buf_i = 0; - for((a = v->area), (a_i = 0); a && len > 0; (a=a->next), (a_i++)) { - if(a->floating) - n = snprintf(&buffer[buf_i], len, "# ~ %d %d\n", - a->rect.width, a->rect.height); - else - n = snprintf(&buffer[buf_i], len, "# %d %d %d\n", - a_i, a->rect.x, a->rect.width); - buf_i += n; - len -= n; - for(f=a->frame; f && len > 0; f=f->anext) { - XRectangle *r = &f->rect; - if(a->floating) - n = snprintf(&buffer[buf_i], len, "~ 0x%x %d %d %d %d %s\n", - (uint)f->client->win, - r->x, r->y, r->width, r->height, - f->client->props); - else - n = snprintf(&buffer[buf_i], len, "%d 0x%x %d %d %s\n", - a_i, (uint)f->client->win, r->y, - r->height, f->client->props); - if(len - n < 0) - return (uchar*)buffer; - buf_i += n; - len -= n; - } - } - return (uchar *)buffer; -} - -Client * -client_of_message(View *v, char *message, uint *next) { - ulong id = 0; - Client *c; - - if(!strncmp(message, "sel ", 4)) { - *next = 4; - return sel_client_of_view(v); - } - sscanf(message, "0x%lx %n", &id, next); - if(!id) - sscanf(message, "%lu %n", &id, next); - if(!id) - return nil; - for(c=client; c && c->win!=id; c=c->next); - return c; -} - -Area * -area_of_message(View *v, char *message, uint *next) { - uint i; - Area *a; - - if(!strncmp(message, "sel ", 4)) { - *next = 4; - return v->sel; - } - if(!strncmp(message, "~ ", 2)) { - *next = 2; - return v->area; - } - if(1 != sscanf(message, "%u %n", &i, next) || i == 0) - return nil; - for(a=v->area; i && a; a=a->next) - i--; - return a; -} - -char * -message_view(View *v, char *message) { - uint n, i; - Client *c; - Frame *f; - Area *a; - Bool swap; - static char Ebadvalue[] = "bad value"; - - if(!strncmp(message, "send ", 5)) { - message += 5; - swap = False; - goto send; - } - if(!strncmp(message, "swap ", 5)) { - message += 5; - swap = True; - goto send; - } - if(!strncmp(message, "select ", 7)) { - message += 7; - return select_area(v->sel, message); - } - if(!strncmp(message, "colmode ", 8)) { - message += 8; - if(!(a = area_of_message(v, message, &n)) || a == v->area) - return Ebadvalue; - if((i = column_mode_of_str(&message[n])) == -1) - return Ebadvalue; - a->mode = i; - arrange_column(a, True); - restack_view(v); - if(v == screen->sel) - focus_view(screen, v); - draw_frames(); - return nil; - } - return Ebadvalue; - -send: - if(!(c = client_of_message(v, message, &n))) - return Ebadvalue; - if(!(f = clientframe_of_view(v, c))) - return Ebadvalue; - return send_client(f, &message[n], swap); -} - -void -update_views() { - View *n, *v; - View *old = screen->sel; - - for(v=view; v; v=v->next) - update_frame_selectors(v); - if(old && !strncmp(old->name, "nil", 4)) - old = nil; - for((v=view) && (n=v->next); v; (v=n) && (n=v->next)) - if((v != old) && is_empty(v)) - destroy_view(v); - if(old) - focus_view(screen, old); - else if(screen->sel) - focus_view(screen, screen->sel); -} - -uint -newcolw_of_view(View *v) { - Rule *r; - Area *a; - uint i, n; - regmatch_t tmpregm; - - for(r=def.colrules.rule; r; r=r->next) { - if(!regexec(&r->regex, v->name, 1, &tmpregm, 0)) { - char buf[256]; - char *toks[16]; - strncpy(buf, r->value, sizeof(buf)); - n = tokenize(toks, 16, buf, '+'); - for(a=v->area, i=0; a; a=a->next) - i++; - if(n && n >= i) { - if(sscanf(toks[i - 1], "%u", &n) == 1) - return (screen->rect.width * n) / 100; - } - break; - } - } - return 0; -} diff --git a/wmii.1 b/wmii.1 @@ -1,205 +0,0 @@ -.TH WMII 1 wmii-VERSION -.SH NAME -wmii \(em window manager improved\(emimproved -.SH SYNOPSIS -.B wmii -.SH DESCRIPTION -.SS Overview -.B wmii -is a dynamic window manager for X11. See -.BR wmiiwm (1) -for more info. -.SS Actions -An action is a shell script in the default setup, but it can actually be -any executable file. It is executed usually by selecting it from the -actions menu. -You can customize an action by copying it from the global action -directory CONFPREFIX/wmii-3.5 to $HOME/.wmii-3.5 and then editing the copy to -fit your needs. Of course you can also create your own actions there; make -sure that they are executable. -.P -Here is a list of the default actions: -.TP 2 -quit -leave the window manager nicely -.TP 2 -status -periodically print date and load average to the bar -.TP 2 -welcome -display a welcome message that contains the wmii tutorial -.TP 2 -wmiirc -configure wmii -.SS Default Key Bindings -.PD 0 -.B Moving Around -.RS 2 -.TP 16 -.I Key -.I Action -.TP -.B Mod-h -Move to a -.B window -to the -.B left -of the one currently focused -.TP -.B Mod-l -Move to a -.B window -to the -.B right -of the one currently focused -.TP -.B Mod-j -Move to a -.B window below -the one currently focused -.TP -.B Mod-k -Move to a -.B window above -the one currently focused -.TP -.B Mod-space -.B Toggle -between the managed and floating -.B layer -.TP -.BI Mod-t \ tag -Move to the -.B view -of the given -.I tag -.TP -.B Mod-[0-9] -Move to the -.B view -with the given number -.PD 1 -.P -.RE -.B Moving Things Around -.RS 2 -.PD 0 -.TP 16 -.I Key -.I Action -.TP -.B Mod-Shift-h -Move the current window -.B window -to a column on the -.B left -.TP -.B Mod-Shift-l -Move the current window -.B window -to a column on the -.B right -.TP -.B Mod-Shift-j -Move the current -.B window below -the one beneath it -.TP -.B Mod-Shift-k -Move the current -.B window above -the one above it -.TP -.B Mod-Shift-space -.B Toggle -the current -.B window -between the managed and floating -.B layer -.TP -.BI Mod-Shift-t \ tag -Move the current window to the -.B view -of the given -.I tag -.TP -.B Mod-Shift-[0-9] -Move to the current window to the -.B view -with the given number -.PD 1 -.P -.RE -.B Miscellaneous -.RS 2 -.PD 0 -.TP 16 -.I Key -.I Action -.TP -.B Mod-m -Switch the current column to -.B max mode -.TP -.B Mod-s -Switch the current column to -.B stack mode -.TP -.B Mod-d -Switch the current column to -.B default mode -.TP -.B Mod-Shift-c -.B Kill -the selected client -.TP -.BI Mod-p \ program -.B Execute -.I program -.TP -.BI Mod-a \ action -Execute the -.B named action -.TP -.B Mod-Enter -Start an -.B xterm - -.SS Configuration -If you feel the need to change the default configuration, then customize (as -described above) the -.B wmiirc -action. This action is executed at the end of the -.B wmii -script and does all the work of setting up the window manager, the key -bindings, the bar labels, etc. -.SH FILES -.TP -/tmp/ns.$USER.${DISPLAY%.0}/wmii -The wmii socket file which provides a 9P service. -.TP -CONFPREFIX/wmii-3.5 -Global action directory. -.TP -$HOME/.wmii-3.5 -User-specific action directory. Actions are first searched here. -.SH ENVIRONMENT -.TP -HOME, DISPLAY -See the section -.B FILES -above. -.P -The following variables are set and exported within -.B wmii -and thus can be used in actions: -.TP -WMII_ADDRESS -Socket file of -.BR wmiiwm (1). -Used by -.BR wmiir (1). -.SH SEE ALSO -.BR wmiiwm (1), -.BR dmenu (1), -.BR wmiir (1) diff --git a/wmii.eps b/wmii.eps @@ -1,29 +0,0 @@ -%!PS-Adobe-2.0 EPSF-1.2 -%%BoundingBox: -1 0 51 27 -%%Creator: MetaPost -%%CreationDate: 2007.02.27:1944 -%%Pages: 1 -%%EndProlog -%%Page: 1 1 - 0 6.23616 dtransform truncate idtransform setlinewidth pop [] 0 setdash - 0 setlinecap 2 setlinejoin 10 setmiterlimit -newpath 2.83461 17.00761 moveto -2.83461 2.83461 lineto -14.17302 2.83461 lineto -14.17302 17.00761 lineto -14.17302 2.83461 lineto -25.51143 2.83461 lineto -25.51143 14.173 lineto -36.84984 14.173 lineto stroke - 0 setlinejoin -newpath 36.84984 14.173 moveto -36.84984 0 lineto -36.84984 14.173 lineto -48.18825 14.173 lineto -48.18825 0 lineto stroke -newpath 36.84984 20.40916 moveto -36.84984 26.07837 lineto stroke -newpath 48.18825 20.40916 moveto -48.18825 26.07837 lineto stroke -showpage -%%EOF diff --git a/wmii.h b/wmii.h @@ -1,451 +0,0 @@ -/* - * © 2004-2006 Anselm R. Garbe <garbeam at gmail dot com> - * See LICENSE file for license details. - */ - -#include <stdio.h> -#include <regex.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> - -#include <ixp.h> - -#define nil ((void*)0) -#define nelem(ary) (sizeof(ary) / sizeof(*ary)) - -/* Types */ -#undef uchar -#undef ushort -#undef uint -#undef ulong -#undef uvlong -#undef vlong -#define uchar _wmiiuchar -#define ushort _wmiiushort -#define uint _wmiiuint -#define ulong _wmiiulong -#define vlong _wmiivlong -#define uvlong _wmiiuvlong -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef unsigned int uint; -typedef unsigned long ulong; -typedef unsigned long long uvlong; -typedef long long vlong; - -#define BLITZ_FONT "-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*-*" -#define BLITZ_FOCUSCOLORS "#ffffff #335577 #447799" -#define BLITZ_NORMCOLORS "#222222 #eeeeee #666666" - -typedef struct Blitz Blitz; -typedef struct BlitzColor BlitzColor; -typedef struct BlitzFont BlitzFont; -typedef struct BlitzBrush BlitzBrush; - -struct Blitz { - Display *dpy; - int screen; - Window root; -}; - -enum BlitzAlign { - NORTH = 0x01, - EAST = 0x02, - SOUTH = 0x04, - WEST = 0x08, - NEAST = NORTH | EAST, - NWEST = NORTH | WEST, - SEAST = SOUTH | EAST, - SWEST = SOUTH | WEST, - CENTER = NEAST | SWEST -}; - -typedef enum BlitzAlign BlitzAlign; - -struct BlitzColor { - vlong bg; - vlong fg; - vlong border; - char colstr[24]; /* #RRGGBB #RRGGBB #RRGGBB */ -}; - -struct BlitzFont { - XFontStruct *xfont; - XFontSet set; - int ascent; - int descent; - uint height; - char *fontstr; -}; - -struct BlitzBrush { - Blitz *blitz; - Drawable drawable; - GC gc; - int border; - BlitzColor color; - BlitzAlign align; - BlitzFont *font; - XRectangle rect; /* relative rect */ -}; - -enum { -/* WM atom */ - WMState, WMProtocols, WMDelete, -/* NET atom */ - NetSupported, NetWMName, -/* Other */ - TagsAtom, -/* Last atom */ - AtomLast -}; - -/* Column modes */ -enum { Coldefault, Colstack, Colmax }; - -/* Cursor */ -enum { CurNormal, CurNECorner, CurNWCorner, CurSECorner, CurSWCorner, - CurMove, CurInput, CurInvisible, CurLast }; - -enum { NCOL = 16 }; -enum { WM_PROTOCOL_DELWIN = 1 }; - -/* Data Structures */ -typedef struct View View; -typedef struct Area Area; -typedef struct Frame Frame; -typedef struct Client Client; -typedef struct Key Key; -typedef struct Bar Bar; -typedef struct Rule Rule; -typedef struct Ruleset Ruleset; -typedef struct WMScreen WMScreen; - -struct View { - View *next; - char name[256]; - ushort id; - Area *area; - Area *sel; - Area *revert; -}; - -struct Area { - Area *next; - Frame *frame; - Frame *stack; - Frame *sel; - View *view; - Bool floating; - ushort id; - int mode; - XRectangle rect; -}; - -struct Frame { - Frame *cnext; - Frame *anext; - Frame *snext; - View *view; - Area *area; - ushort id; - XRectangle rect; - XRectangle crect; - XRectangle revert; - Client *client; - Bool collapsed; - XRectangle grabbox; - XRectangle titlebar; -}; - -struct Client { - Client *next; - Area *revert; - Frame *frame; - Frame *sel; - char name[256]; - char tags[256]; - char props[512]; - uint border; - int proto; - Bool floating; - Bool fixedsize; - Bool fullscreen; - Bool urgent; - Bool mapped; - Bool frame_mapped; - int unmapped; - Window win; - Window trans; - Window framewin; - Cursor cursor; - XRectangle rect; - XSizeHints size; - GC gc; -}; - -struct Key { - Key *next; - Key *lnext; - Key *tnext; - ushort id; - char name[128]; - ulong mod; - KeyCode key; -}; - -struct Bar { - Bar *next; - Bar *smaller; - char buf[280]; - char text[256]; - char name[256]; - ushort id; - BlitzBrush brush; -}; - -struct Rule { - Rule *next; - regex_t regex; - char value[256]; -}; - -struct Ruleset { - Rule *rule; - char *string; - uint size; -}; - -/* global variables */ -struct { - BlitzColor focuscolor; - BlitzColor normcolor; - BlitzFont font; - uint border; - uint snap; - char *keys; - uint keyssz; - Ruleset tagrules; - Ruleset colrules; - char grabmod[5]; - ulong mod; - int colmode; -} def; - -enum { BarLeft, BarRight }; - -struct WMScreen { - Bar *bar[2]; - View *sel; - Client *focus; - Client *hasgrab; - Window barwin; - - XRectangle rect; - XRectangle brect; - BlitzBrush bbrush; -} *screens, *screen; - -Client *client; -View *view; -Key *key; -Client c_magic; -Client c_root; - -enum { BUFFER_SIZE = 8092 }; -char buffer[BUFFER_SIZE]; - -/* IXP */ -IXPServer srv; -P9Srv p9srv; - -/* X11 */ -uint num_screens; -Blitz blz; -GC xorgc; -char *user; -Atom atom[AtomLast]; -Cursor cursor[CurLast]; -uint valid_mask; -uint num_lock_mask; -Bool sel_screen; -Pixmap pmap; -void (*handler[LASTEvent]) (XEvent *); - -/* Misc */ -Bool starting; -Bool verbose; -char *execstr; - -/* wm.c */ -char *message_root(char *message); - -/* area.c */ -Area *create_area(View *v, Area *pos, uint w); -void destroy_area(Area *a); -Area *area_of_id(View *t, ushort id); -void focus_area(Area *a); -char *select_area(Area *a, char *arg); -void send_to_area(Area *to, Frame *f); -void attach_to_area(Area *a, Frame *f, Bool send); -void detach_from_area(Frame *f); -Client *sel_client_of_area(Area *a); - -/* bar.c */ -Bar *create_bar(Bar **b_link, char *name); -void destroy_bar(Bar **b_link, Bar *b); -void draw_bar(WMScreen *s); -void draw_border(BlitzBrush *b); -void resize_bar(); -Bar *bar_of_name(Bar *b_link, const char *name); - -/* client.c */ -Client *create_client(Window w, XWindowAttributes *wa); -void destroy_client(Client *c); -void configure_client(Client *c); -void prop_client(Client *c, Atom a); -void kill_client(Client *c); -void gravitate_client(Client *c, Bool invert); -void map_client(Client *c); -void unmap_client(Client *c, int state); -void map_frame(Client *c); -void unmap_frame(Client *c); -void set_cursor(Client *c, Cursor cur); -void focus_frame(Frame *f, Bool restack); -void reparent_client(Client *c, Window w, int x, int y); -void manage_client(Client *c); -void focus(Client *c, Bool restack); -void focus_client(Client *c); -void resize_client(Client *c, XRectangle *r); -void apply_sizehints(Client *c, XRectangle *r, Bool floating, Bool frame, BlitzAlign sticky); -char *send_client(Frame *f, char *arg, Bool swap); -char * message_client(Client *c, char *message); -void move_client(Client *c, char *arg); -void size_client(Client *c, char *arg); -Client *sel_client(); -Frame *frame_of_win(Window w); -Client *client_of_win(Window w); -void update_client_grab(Client *c); -void apply_rules(Client *c); -void apply_tags(Client *c, const char *tags); - -/* column.c */ -void arrange_column(Area *a, Bool dirty); -void resize_column(Client *c, XRectangle *r); -int column_mode_of_str(char *arg); -char *str_of_column_mode(int mode); -Area *new_column(View *v, Area *pos, uint w); - -/* draw.c */ -int loadcolor(Blitz *blitz, BlitzColor *c); -void draw_label(BlitzBrush *b, char *text); -void draw_tile(BlitzBrush *b); -void draw_rect(BlitzBrush *b); - -void drawbg(Display *dpy, Drawable drawable, GC gc, - XRectangle *rect, BlitzColor c, Bool fill, Bool border); -void drawcursor(Display *dpy, Drawable drawable, GC gc, - int x, int y, uint h, BlitzColor c); -uint textwidth(BlitzFont *font, char *text); -uint textwidth_l(BlitzFont *font, char *text, uint len); -void loadfont(Blitz *blitz, BlitzFont *font); -uint labelh(BlitzFont *font); -char *parse_colors(char **buf, int *buflen, BlitzColor *col); - -/* event.c */ -void dispatch_event(XEvent *e); -void check_x_event(IXPConn *c); -uint flushevents(long even_mask, Bool dispatch); - -/* frame.c */ -Frame *create_frame(Client *c, View *v); -void remove_frame(Frame *f); -void insert_frame(Frame *pos, Frame *f, Bool before); -void resize_frame(Frame *f, XRectangle *r); -Bool frame_to_top(Frame *f); -void set_frame_cursor(Frame *f, int x, int y); -void swap_frames(Frame *fa, Frame *fb); -int frame_delta_h(); -void frame2client(XRectangle *r); -void client2frame(XRectangle *r); -void draw_frame(Frame *f); -void draw_frames(); -void update_frame_widget_colors(Frame *f); -void check_frame_constraints(XRectangle *rect); - -/* fs.c */ -void fs_attach(P9Req *r); -void fs_clunk(P9Req *r); -void fs_create(P9Req *r); -void fs_flush(P9Req *r); -void fs_freefid(Fid *f); -void fs_open(P9Req *r); -void fs_read(P9Req *r); -void fs_remove(P9Req *r); -void fs_stat(P9Req *r); -void fs_walk(P9Req *r); -void fs_write(P9Req *r); -void write_event(char *format, ...); - -/* geom.c */ -Bool ptinrect(int x, int y, XRectangle * r); -BlitzAlign quadrant(XRectangle *rect, int x, int y); -Cursor cursor_of_quad(BlitzAlign align); -int strtorect(XRectangle *r, const char *val); -BlitzAlign get_sticky(XRectangle *src, XRectangle *dst); -int r_east(XRectangle *r); -int r_south(XRectangle *r); - -/* key.c */ -void kpress(Window w, ulong mod, KeyCode keycode); -void update_keys(); -void init_lock_keys(); -ulong mod_key_of_str(char *val); - -/* mouse.c */ -void do_mouse_resize(Client *c, Bool grabbox, BlitzAlign align); -void grab_mouse(Window w, ulong mod, ulong button); -void ungrab_mouse(Window w, ulong mod, uint button); -BlitzAlign snap_rect(XRectangle *rects, int num, XRectangle *current, - BlitzAlign *mask, int snap); -void grab_button(Window w, uint button, ulong mod); - -/* rule.c */ -void update_rules(Rule **rule, const char *data); -void trim(char *str, const char *chars); - -#define strlcat wmii_strlcat -/* util.c */ -uint tokenize(char *res[], uint reslen, char *str, char delim); -char *estrdup(const char *str); -void *erealloc(void *ptr, uint size); -void *emallocz(uint size); -void *emalloc(uint size); -void fatal(const char *fmt, ...); -int max(int a, int b); -int min(int a, int b); -char *str_nil(char *s); -uint strlcat(char *dst, const char *src, unsigned int siz); - -/* view.c */ -void arrange_view(View *v); -void scale_view(View *v, float w); -View *get_view(const char *name); -View *create_view(const char *name); -void focus_view(WMScreen *s, View *v); -void update_client_views(Client *c, char **tags); -XRectangle *rects_of_view(View *v, uint *num, Frame *ignore); -View *view_of_id(ushort id); -void select_view(const char *arg); -void attach_to_view(View *v, Frame *f); -Client *sel_client_of_view(View *v); -char *message_view(View *v, char *message); -void restack_view(View *v); -uchar *view_index(View *v); -void destroy_view(View *v); -void update_views(); -uint newcolw_of_view(View *v); - -/* wm.c */ -int wmii_error_handler(Display *dpy, XErrorEvent *error); -int win_proto(Window w); diff --git a/wmii.mp b/wmii.mp @@ -1,19 +0,0 @@ -beginfig(1) -%u=0.6cm; -u=0.2cm; -h=3u; -space=u; -linecap:=butt; -linejoin:=beveled; -pickup pencircle scaled 1.1u; -draw (.5u,h)--(.5u,.5u)--(1.5u+space,.5u)--(1.5u+space,h)--(1.5u+space,.5u)--(2.5u+2space,.5u)\ - --(2.5u+2space,h-.5u)--(3.5u+3space,h-.5u); -linejoin:=mitered; -draw (3.5u+3space,h-.5u)--(3.5u+3space,0)--(3.5u+3space,h-.5u)--(4.5u+4space,h-.5u)--(4.5u+4space,0);; - -gap=.6u; -draw (3.5u+3space,h+gap)--(3.5u+3space,h+u+gap); -draw (4.5u+4space,h+gap)--(4.5u+4space,h+u+gap); -endfig - -end diff --git a/wmii9rc b/wmii9rc @@ -1,19 +0,0 @@ -#!/bin/sh -f -RC="" -for i in "$PLAN9" P9PATHS; do - if [ -d "$i" -a -x "$i/bin/rc" ]; then - export PLAN9="$i" - RC="$i/bin/rc" - break; - fi -done - -if [ ! -n "$RC" ]; then - exit 1 -fi - -if [ -n "$1" ]; then - exec $RC $@ -else - true -fi diff --git a/wmiiloop b/wmiiloop @@ -1,35 +0,0 @@ -#!AWKPATH -f -BEGIN { - arg[1] = "Nop" - body = ""; - writekeys = "wmiir write /keys" - print "IFS=''" -} - -function addevent() { - if(arg[1] == "Key") - keys[arg[2]] = 1; - - var = arg[1] "s" - print var "=\"$" var " " arg[2] "\"" - - gsub("[^a-zA-Z_0-9]", "_", arg[2]); - if(body != "") - print arg[1] "_" arg[2] "() {" body "\n}" -} - -/^(Event|Key|Action)[ \t]/ { - addevent() - split($0, arg) - body = "" -} -/^[ \t]/ { - body = body"\n"$0 -} - -END { - addevent() - for(key in keys) - print key | writekeys; - close(writekeys); -} diff --git a/wmiiloop.1 b/wmiiloop.1 @@ -1,79 +0,0 @@ -.TH WMIIR 1 wmii\-VERSION -.SH NAME -wmiiloop \(em wmii event loop processor -.SH SYNOPSIS -.B cat -.I events -| -.B wmiiloop -| -.B sh -.br -.SH DESCRIPTION -.SS Overview -.B wmiiloop -is an awk script which preprocesses -.BR sh (1) -syntax to simplify writing a wmii event loop. - -.br -.B eventloop -sends any text up to the first line containing -.I # -.IR Events|Actions|Key , -and any text after any other line begining with -.IR # , -directly to its standard output. Any other line is processed based -on its first word, with every indented line after it taken as its -.IR body . -.TP 2 -.BI "Key " sequence -The key sequence -.I sequence -is bound to its -.IR body . -.I sequence -is written to -.I /keys -and -.I body -is executed, with -.I $key -set to -.I sequence -whenever the key is pressed. - -.TP 2 -.BI "Event " name -Whenever an event with -.I name -is read from -.IR /event , -.I body -is executed, with -.I $event -set to -.IR name , -and -.I $[1\-9] -set to further arguments. - -.TP 2 -.BI "Action " name -A function called -.BI Action_ name -is created with -.I body -as its body, and -.I name -is added to the -.I $ACTIONS -variable. - -.SH SEE ALSO -.BR wmii (1), -.BR wmiir (1), -.BR wmiiwm (1) -.B PREFIX/etc/wmiirc - -http://www.cs.bell\-labs.com/sys/man/5/INDEX.html diff --git a/wmiir b/wmiir @@ -1,2 +0,0 @@ -#!/bin/sh -f -exec ixpc -a "$WMII_ADDRESS" $@ diff --git a/wmiir.1 b/wmiir.1 @@ -1,51 +0,0 @@ -.TH WMIIR 1 wmii-VERSION -.SH NAME -wmiir \(em wrapper script for ixpc -.SH SYNOPSIS -.B wmiir -.RB [ \-a -.IR address ] -.I action -.I file -.br -.B wmiir -.B \-v -.SH DESCRIPTION -.SS Overview -.B wmiir -is a wrapper script for -.BR ixpc (1) -to conveniently access the filesystem of -.BR wmiiwm (1) -from the command line or from shell -scripts. It can be used to configure -.BR wmii (1). - -.SS Options -See the manual page of -.BR ixpc (1) . -.SH ENVIRONMENT -.TP -WMII_ADDRESS -The address of the wmiiwm file server to which wmiir will connect. -.SH EXAMPLES -.TP -.B wmiir ls / -This prints the root directory of the wmii filesystem. For more information -about the contents of this filesystem, see -.BR wmiiwm (1). -.TP -.B echo -n quit | wmiir write /ctl -Write 'quit' to the main control file of the wmii filesystem, effectively -leaving wmii. -.TP -.B echo -n view 2 | wmiir write /ctl -Bring into view all clients tagged '2'. To learn about clients and -tags, see -.BR wmii (1). -.SH SEE ALSO -.BR wmii (1), -.BR ixpc (1), -.BR wmiiwm (1) - -http://www.cs.bell-labs.com/sys/man/5/INDEX.html diff --git a/wmiistartrc b/wmiistartrc @@ -1,13 +0,0 @@ -#!/bin/sh -f -# start wmiirc - -export WMII_CONFPATH="$HOME/.wmii-CONFVERSION:CONFPREFIX/wmii-CONFVERSION" - -if wmii9rc; then - WMIIRC=`PATH="$WMII_CONFPATH:$PATH" which rc.wmii` -else - WMIIRC=`PATH="$WMII_CONFPATH:$PATH" which wmiirc` -fi - -mkdir $HOME/.wmii-CONFVERSION 2>/dev/null && CONFPREFIX/wmii-CONFVERSION/welcome & -exec "$WMIIRC" $@ diff --git a/wmiiwm.1 b/wmiiwm.1 @@ -1,150 +0,0 @@ -.TH WMIIWM 1 wmii-VERSION -.SH NAME -wmiiwm \(em window manager improved\(emimproved (core) -.SH SYNOPSIS -.B wmiiwm -.B \-a -.I <address> -.RB [ \-c ] -.RB [ \-v ] -.SH DESCRIPTION - -.PD 0 -.SS Overview -.BR wmiiwm (1) -is the core of window manager improved\(emimproved. -.P -.B wmii -is a dynamic window manager for X11. In contrast to static window management -the user rarely has to think about how to organize windows, no matter what he -is doing or how many applications are used at the same time. The window manager -adapts to the current environment and fits to the needs of the user, rather -than forcing him to use a preset, fixed layout and trying to shoehorn all -windows and applications into it. -.P -.B wmii -supports classic and tiled window management with extended keyboard and mouse -control. The classic window management arranges windows in a floating layer -in which windows can be moved and resized freely. The tiled window management -is based on columns which split up the screen horizontally. Each column handles -arbitrary windows and arranges them vertically in a non\-overlapping way. They -can then be moved and resized between and within columns at will. -.P -.B wmii -provides a virtual filesystem which represents the internal state similar to -the procfs of Unix operating systems. Modifying this virtual filesystem results -in changing the state of the window manager. The virtual filesystem service can -be accessed through 9P\-capable client programs, like -.BR wmiir (1) . -This allows simple and powerful remote control of the core window manager. -.P -.B wmii -basically consists of clients, columns, views, and the bar, which are described -in detail in the -.B Terminology -section. -.SS Options -.TP -.BI \-a " address" -Lets you specify the address which -.B wmiiwm -uses to listen for connections. The syntax for -.I address -is taken (along with many other profound ideas) from the Plan 9 operating -system and has the form -.B unix!/path/to/socket -for unix socket files, and -.B tcp!hostname!port -for tcp sockets. -.TP -.B \-c -Checks if another window manager is running. If not it exits with termination code -0. -.TP -.B \-v -Prints version information to stdout, then exits. -.SS Terminology -.TP 2 -Display -A running X server instance consisting of input devices and screens. -.TP 2 -Screen -A physical or virtual (Xinerama or -.BR Xnest (1)) -screen of an X display. A screen displays a bar window and a view at a time. -.TP 2 -Window -A (rectangular) drawable X object which is displayed on a screen, usually an -application window. -.TP 2 -Client -An application window surrounded by a frame window containing a border and a -title\-bar. -.TP 2 -Floating layer -A screen layer of -.B wmii -on top of all other layers, where clients are arranged in a classic (floating) -way. They can be resized or moved freely. -.TP 2 -Managed layer -A screen layer of -.B wmii -behind the floating layer, where clients are arranged in a non\-overlapping -(managed) way. Here, the window manager dynamically assigns each client a -size and position. The managed layer consists of columns. -.TP 2 -Tag -Alphanumeric strings which can be assigned to a client. This provides a -mechanism to group clients with similar properties. Clients can have one -tag, e.g. -.IR work , -or several tags, e.g. -.IR work+mail . -Tags are separated with the -.I + -character. -.TP 2 -View -A set of clients containing a specific tag, quite similiar to a workspace in -other window managers. It consists of the floating and managed layers. -.TP 2 -Column -A column is a screen area which arranges clients vertically in a -non\-overlapping way. Columns provide three different modes, which arrange -clients with equal size, stacked, or maximized respectively. Clients can be -moved and resized between and within columns freely. -.TP 2 -Bar -The bar at the bottom of the screen displays a label for each view and -allows the creation of arbitrary user\-defined labels. -.TP 2 -Event -An event is a message which can be read from a special file in the filesystem -of -.BR wmiiwm , -such as a mouse button press, a key press, or a message written by a different -9P\-client. -.SS Basic window management -Running a raw -.B wmiiwm -process without the -.BR wmii (1) -script provides basic window management capabilities already. However to use -it effectively, remote control through its filesystem interface is necessary. -By default it is only usable with the mouse in conjunction with the -.I Mod1 (Alt) -modifier key. Other interactions like customizing the style, killing or -retagging clients, or grabbing keys cannot be achieved without accessing the -filesystem. -.P -The filesystem can be accessed by connecting to the -.I address -of -.B wmiiwm -with any 9P\-capable client, like -.BR wmiir (1). -.SH SEE ALSO -.BR wmii (1), -.BR dmenu (1), -.BR wmiir (1)