/* $Id$ */
/*
* Copyright (c) 2007 Andreas Jaggi <andreas.jaggi@waterwave.ch>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "x-wm.h"
#include "client.h"
Display* display;
int screen;
Window root;
Window focus;
Atom wm_delete_window;
Atom wm_protocols;
int clientcounter = 0;
int quit = 0;
char* startscript = NULL;
void grab_keys();
void grab_buttons();
void loop();
void handle_motion(XMotionEvent* ev);
void handle_crossing(XCrossingEvent* ev);
void handle_focus(XFocusChangeEvent* ev);
void handle_create(XCreateWindowEvent* ev);
void handle_destroy(XDestroyWindowEvent* ev);
void handle_buttonPress(XButtonEvent* ev);
void handle_keyPress(XKeyEvent* ev);
void close_window(Window w);
int main ( int argc, char* argv[] ) {
Window dw1, dw2;
Window* existing_windows;
int i, win_count;
int r = -1;
int status;
XSetWindowAttributes attr;
while ( (r = getopt(argc, argv, "hlvs:")) != -1 ) {
switch ( r ) {
case 'h':
usage();
exit(0);
case 'l':
license();
exit(0);
case 'v':
version();
exit(0);
case 's':
startscript = optarg;
break;
default:
usage();
exit(1);
}
}
fprintf(stdout, "starting x-wm...\n");
if ( startscript != NULL ) {
fprintf(stdout, "x-wm: running startscript: %s...\n", startscript);
switch ( fork() ) {
case 0:
execlp(startscript,startscript,0);
case -1:
fprintf(stderr, "x-wm: could not fork()!\n");
default:
wait(&status);
if ( WIFEXITED(status) ) {
fprintf(stderr, "x-wm: startscript (%s) ended with exit(%d)\n", startscript, WEXITSTATUS(status));
}
if ( WIFSIGNALED(status) ) {
fprintf(stderr, "x-wm: startscript (%s) received signal %d\n", startscript, WTERMSIG(status));
}
}
}
if ( !(display = XOpenDisplay(NULL)) ) {
fprintf(stderr, "x-wm: cannot open display\n");
exit(1);
}
screen = DefaultScreen(display);
root = RootWindow(display, screen);
attr.event_mask = 0
//| SubstructureRedirectMask
//| SubstructureNotifyMask
| StructureNotifyMask
//| PropertyChangeMask
| EnterWindowMask
//| LeaveWindowMask
| ButtonPressMask
//| ButtonReleaseMask
| KeyPressMask
//| KeyReleaseMask
//| FocusChangeMask
//| PointerMotionMask
//| PointerMotionHintMask
//| Button1MotionMask
//| Button2MotionMask
//| Button3MotionMask
//| Button4MotionMask
//| Button5MotionMask
//| ButtonMotionMask
;
XChangeWindowAttributes(display, root, CWEventMask, &attr);
wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
wm_protocols = XInternAtom(display, "WM_PROTOCOLS", False);
grab_keys();
grab_buttons();
init_clients();
XQueryTree(display, root, &dw1, &dw2, &existing_windows, &win_count);
fprintf(stderr, "x-wm: win_count = %d\n", win_count);
for ( i = 0; i < win_count; i++ ) {
if ( existing_windows[i] != root ) {
clientcounter++;
create_client(existing_windows[i]);
}
}
XFree(existing_windows);
fprintf(stderr, "x-wm: clientcount = %d\n", clientcounter);
loop();
exit(0);
}
void grab_buttons ( ) {
/* listen to mouse clicks into the root window */
XGrabButton(display, Button1, 0, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
/* listen to mouse clicks into the root window */
XGrabButton(display, Button1, (Mod1Mask|ControlMask), root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
/* listen to scroll wheel + ALT */
XGrabButton(display, Button4, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(display, Button5, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
/* listen to scroll wheel + ALT + Ctrl (+ Shift) */
XGrabButton(display, Button4, (Mod1Mask|ControlMask), root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(display, Button5, (Mod1Mask|ControlMask), root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(display, Button4, (Mod1Mask|ControlMask|ShiftMask), root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(display, Button5, (Mod1Mask|ControlMask|ShiftMask), root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
}
void grab_keys ( ) {
/* listen to Alt+Tab in the root window */
XGrabKey(display, XKeysymToKeycode(display, XStringToKeysym("Tab")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
/* listen to Alt+F4 in the root window */
XGrabKey(display, XKeysymToKeycode(display, XStringToKeysym("F4")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
/* listen to Alt+F2 in the root window */
XGrabKey(display, XKeysymToKeycode(display, XStringToKeysym("F2")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
/* listen to Alt+UP in the root window */
XGrabKey(display, XKeysymToKeycode(display, XStringToKeysym("Up")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
/* listen to Alt+DOWN in the root window */
XGrabKey(display, XKeysymToKeycode(display, XStringToKeysym("Down")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
/* listen to Alt+LEFT in the root window */
XGrabKey(display, XKeysymToKeycode(display, XStringToKeysym("Left")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
/* listen to Alt+RIGHT in the root window */
XGrabKey(display, XKeysymToKeycode(display, XStringToKeysym("Right")), Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
}
void loop ( ) {
XEvent ev;
while ( !quit ) {
XNextEvent(display, &ev);
switch ( ev.type ) {
case MotionNotify:
handle_motion(&(ev.xmotion));
break;
case ButtonPress:
handle_buttonPress(&(ev.xbutton));
break;
case KeyPress:
handle_keyPress(&(ev.xkey));
break;
case EnterNotify:
handle_crossing(&(ev.xcrossing));
break;
case CreateNotify:
handle_create(&(ev.xcreatewindow));
break;
case DestroyNotify:
handle_destroy(&(ev.xdestroywindow));
break;
default:
fprintf(stderr, "x-wm: received XEvent of unknown type %d\n", ev.type);
}
}
}
void handle_destroy( XDestroyWindowEvent* ev ) {
fprintf(stdout, "x-wm: received XDestroyWindowEvent event\n");
clientcounter--;
fprintf(stderr, "x-wm: clientcounter: %d\n", clientcounter);
destroy_client(ev->window);
if ( ev->window == root ) {
fprintf(stderr, "x-wm: root window destroyed, quitting...\n");
quit = 1;
}
if ( clientcounter <= 0 ) {
fprintf(stderr, "x-wm: clientcounter <= 0 (%d), quitting...\n", clientcounter);
quit = 1;
}
}
void handle_create ( XCreateWindowEvent* ev ) {
fprintf(stdout, "x-wm: received XCreateWindowEvent event\n");
if ( find_client(ev->window) == NULL ) {
create_client(ev->window);
}
XAddToSaveSet(display, ev->window);
XRaiseWindow(display, ev->window);
XSetInputFocus(display, ev->window, RevertToPointerRoot, CurrentTime);
clientcounter++;
fprintf(stderr, "x-wm: clientcounter: %d\n", clientcounter);
}
void handle_focus ( XFocusChangeEvent* ev ) {
/* TODO */
fprintf(stdout, "x-wm: received XFocusChangeEvent event\n");
}
void handle_crossing ( XCrossingEvent* ev ) {
fprintf(stdout, "x-wm: received XCrossingEvent event: state %d\n", ev->state);
if ( ev->type == EnterNotify ) {
XUngrabButton(display, Button1, 0, root);
fprintf(stdout, "x-wm: received EnterNotify event: state %d\n", ev->state);
XSetInputFocus(display, ev->window, RevertToPointerRoot, ev->time);
}
if ( ev->type == LeaveNotify ) {
fprintf(stdout, "x-wm: received LeaveNotify event: state %d\n", ev->state);
XGrabButton(display, Button1, 0, root, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
}
}
void handle_motion ( XMotionEvent* ev ) {
/* TODO */
fprintf(stdout, "x-wm: received MotionNotify event: state %d\n", ev->state);
}
void handle_buttonPress ( XButtonEvent* ev ) {
Window foo;
int bar,width,height;
XWindowAttributes winattr;
Client* c;
fprintf(stdout, "x-wm: received ButtonPress event: button %d, state %d\n", ev->button, ev->state);
while ( XCheckTypedEvent(display, ButtonPress, (XEvent*) ev) ) { }
if ( XQueryPointer(display, root, &foo, &focus, &bar, &bar, &bar, &bar, &bar) == BadWindow ) {
fprintf(stderr, "x-wm: received BadWindow from XQueryPointer\n");
}
/* cycle through windows with scroll wheel + Alt */
if ( ev->button == Button4 && ev->state == Mod1Mask ) {
if ( XCirculateSubwindowsUp(display, root) == BadWindow ) {
fprintf(stderr, "x-wm: error while cycling upwards through windows (BadWindow)\n");
} else {
fprintf(stdout, "x-wm: cycle through windows\n");
}
}
if ( ev->button == Button5 && ev->state == Mod1Mask ) {
if ( XCirculateSubwindowsDown(display, root) == BadWindow ) {
fprintf(stderr, "x-wm: error while cycling downwards through windows (BadWindow)\n");
} else {
fprintf(stdout, "x-wm: cycle through windows\n");
}
}
if ( ev->button == Button1 ) {
switch ( ev->state ) {
case (Mod1Mask|ControlMask):
/* click into some window -> raise it */
fprintf(stdout, "x-wm: raise window\n");
XRaiseWindow(display, focus);
XSetInputFocus(display, focus, RevertToPointerRoot, CurrentTime);
//XSendEvent(display, focus, False, NoEventMask, (XEvent*)ev);
break;
case 0:
if ( focus == root || focus == None ) {
/* click into root window -> launch xterm */
fprintf(stdout, "x-wm: launch xterm\n");
if ( fork() == 0 ) {
execlp("xterm","xterm",0);
fprintf(stderr, "x-wm: failed to launch xterm\n");
}
}
break;
}
}
if ( focus == None ) {
return;
}
/* resize focused window veticaly width scroll wheel + ALT + Ctrl */
if ( (ev->button == Button4 || ev->button == Button5) && ev->state == (Mod1Mask|ControlMask) ) {
XGetWindowAttributes(display, focus, &winattr);
width = winattr.width;
height = winattr.height;
if ( ev->button == Button4 && height >= 10 ) {
XResizeWindow(display, focus, width, height-10);
if ( (c = find_client(focus)) != NULL ) {
c->maximized = 0;
}
}
if ( (c = find_client(focus)) == NULL || c->maximized == 0 ) {
if ( ev->button == Button5 && height+winattr.y <= DisplayHeight(display, screen)-10 ) {
XResizeWindow(display, focus, width, height+10);
}
}
}
/* resize focused window horizontaly width scroll wheel + ALT + Ctrl + Shift */
if ( (ev->button == Button4 || ev->button == Button5) && ev->state == (Mod1Mask|ControlMask|ShiftMask) ) {
XGetWindowAttributes(display, focus, &winattr);
width = winattr.width;
height = winattr.height;
if ( ev->button == Button4 && width >= 10) {
XResizeWindow(display, focus, width-10, height);
if ( (c = find_client(focus)) != NULL ) {
c->maximized = 0;
}
}
if ( (c = find_client(focus)) == NULL || c->maximized == 0 ) {
if ( ev->button == Button5 && width+winattr.x <= DisplayWidth(display, screen)-10) {
XResizeWindow(display, focus, width+10, height);
}
}
}
}
void handle_keyPress ( XKeyEvent* ev ) {
Window foo;
int bar,x,y,width,height;
Client* c;
XWindowAttributes winattr;
fprintf(stdout, "x-wm: received KeyPress event: keycode %d, state %d\n", ev->keycode, ev->state);
if ( XQueryPointer(display, root, &foo, &focus, &bar, &bar, &bar, &bar, &bar) == BadWindow ) {
fprintf(stderr, "x-wm: received BadWindow from XQueryPointer\n");
}
if ( ev->keycode == XKeysymToKeycode(display, XStringToKeysym("Tab")) && ev->state == Mod1Mask ) {
/* cycle trough subwindows of root with Alt-Tab */
if ( XCirculateSubwindowsUp(display, root) == BadWindow ) {
fprintf(stderr, "x-wm: error while cycling through windows (BadWindow)\n");
} else {
fprintf(stdout, "x-wm: cycle through windows\n");
}
}
if ( focus == None ) {
return;
}
if ( ev->keycode == XKeysymToKeycode(display, XStringToKeysym("F4")) && ev->state == Mod1Mask ) {
/* close window with Alt-F4 */
fprintf(stdout, "x-wm: received close request for a window (Alt-F4)\n");
close_window(focus);
}
if ( ev->keycode == XKeysymToKeycode(display, XStringToKeysym("F2")) && ev->state == Mod1Mask ) {
/* maximize/restore window with Alt-F2 */
width = DisplayWidth(display, screen);
height = DisplayHeight(display, screen);
x = 0;
y = 0;
c = find_client(focus);
if ( c == NULL ) {
fprintf(stdout, "x-wm: found window without client struct\n");
c = create_client(focus);
}
if ( c->maximized ) {
fprintf(stdout, "x-wm: unmaximize window\n");
c->maximized = 0;
x = c->x;
y = c->y;
width = c->width;
height = c->height;
} else {
XGetWindowAttributes(display, focus, &winattr);
fprintf(stdout, "x-wm: maximize window\n");
c->maximized = 1;
c->width = winattr.width;
c->height = winattr.height;
c->x = winattr.x;
c->y = winattr.y;
}
XMoveResizeWindow(display, focus, x, y, width, height);
}
if ( ev->keycode == XKeysymToKeycode(display, XStringToKeysym("Up")) && ev->state == Mod1Mask ) {
/* move window 'up' with Alt+UP */
if ( (c = find_client(focus)) == NULL || c->maximized == 0 ) {
XGetWindowAttributes(display, focus, &winattr);
if ( winattr.y >= 10 ) {
XMoveWindow(display, focus, winattr.x, winattr.y-10);
}
}
}
if ( ev->keycode == XKeysymToKeycode(display, XStringToKeysym("Down")) && ev->state == Mod1Mask ) {
/* move window 'down' with Alt+Down */
if ( (c = find_client(focus)) == NULL || c->maximized == 0 ) {
XGetWindowAttributes(display, focus, &winattr);
if ( winattr.y <= DisplayHeight(display, screen) - winattr.height - 10 ) {
XMoveWindow(display, focus, winattr.x, winattr.y+10);
}
}
}
if ( ev->keycode == XKeysymToKeycode(display, XStringToKeysym("Left")) && ev->state == Mod1Mask ) {
/* move window 'left' with Alt+Left */
if ( (c = find_client(focus)) == NULL || c->maximized == 0 ) {
XGetWindowAttributes(display, focus, &winattr);
if ( winattr.x >= 10 ) {
XMoveWindow(display, focus, winattr.x-10, winattr.y);
}
}
}
if ( ev->keycode == XKeysymToKeycode(display, XStringToKeysym("Right")) && ev->state == Mod1Mask ) {
/* move window 'right' with Alt+Right */
if ( (c = find_client(focus)) == NULL || c->maximized == 0 ) {
XGetWindowAttributes(display, focus, &winattr);
if ( winattr.x <= DisplayWidth(display, screen) - winattr.width - 10 ) {
XMoveWindow(display, focus, winattr.x+10, winattr.y);
}
}
}
}
void close_window ( Window w ) {
XEvent ev;
int protocols_count, i;
Atom* protocols;
if ( XGetWMProtocols(display, w, &protocols, &protocols_count) ) {
for ( i = 0; i < protocols_count; i++ ) {
if ( protocols[i] == wm_delete_window ) {
ev.type = ClientMessage;
ev.xclient.window = w;
ev.xclient.message_type = wm_protocols;
ev.xclient.format = 32;
ev.xclient.data.l[0] = wm_delete_window;
ev.xclient.data.l[1] = CurrentTime;
XSendEvent(display, w, False, NoEventMask, &ev);
XFree(protocols);
return;
}
}
if ( protocols ) {
XFree(protocols);
}
}
XKillClient(display, w);
}