/* $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); }