#include #include #include #include #include #include #include #include #ifdef HAVE_X11_EXTENSIONS_SHAPE_H #include #endif #ifdef AMIGAOS #include #endif #ifdef HAVE_FCNTL_H #include #endif #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef USE_FONTSETS #include #endif #include "drawinfo.h" #include "screen.h" #include "icon.h" #include "client.h" #include "prefs.h" #include "module.h" #include "icc.h" #include "libami.h" #ifdef AMIGAOS #include extern struct Library *XLibBase; struct timeval { long tv_sec; long tv_usec; }; #define fd_set XTransFdset #undef FD_ZERO #undef FD_SET #define FD_ZERO XTransFdZero #define FD_SET XTransFdSet #define select XTransSelect #endif #define HYSTERESIS 5 typedef struct _DragIcon { Icon *icon; Window w; Pixmap pm; int x, y; } DragIcon; Display *dpy = NULL; Client *activeclient=NULL, *clickclient=NULL; Window clickwindow=None; Scrn *menuactive=NULL; Bool shape_extn=False; char *x_server=NULL; char *free_screentitle=NULL; XContext client_context, screen_context, icon_context, menu_context, vroot_context; char *progname; Cursor wm_curs; static int signalled=0, forcemoving=0; static Time last_icon_click=0, last_double=0; static Client *doubleclient=NULL; static int initting=0; static int ignore_badwindow=0; static int dblClickTime=1500; static fd_set master_fd_set; static int max_fd=0; static Window *checkwins; static int shape_event_base, shape_error_base; static int server_grabs=0; static unsigned int meta_mask, switch_mask; static char **main_argv; extern Scrn *mbdclick, *mbdscr; extern void reparent(Client *); extern void redraw(Client *, Window); extern void redrawclient(Client *); extern void redrawmenubar(Scrn *, Window); extern void resizeclientwindow(Client *c, int, int); extern void gadgetclicked(Client *c, Window w, XEvent *e); extern void gadgetunclicked(Client *c, XEvent *e); extern void gadgetaborted(Client *c); extern void clickenter(void); extern void clickleave(void); extern void menu_on(void); extern void menu_off(void); extern void menubar_enter(Window); extern void menubar_leave(Window); extern void *getitembyhotkey(KeySym); extern void menuaction(void *); extern Scrn *getscreenbyroot(Window); extern void assimilate(Window, int, int); extern void deselect_all_icons(Scrn *); extern void reparenticon(Icon *, Scrn *, int, int); extern void handle_client_message(Client *, XClientMessageEvent *); extern void handle_module_input(fd_set *); extern int dispatch_event_to_broker(XEvent *, unsigned long, struct module *); extern void reshape_frame(Client *c); extern void read_rc_file(char *filename, int manage_all); extern void init_modules(); extern void flushmodules(); extern void raiselowerclient(Client *, int); #ifndef AMIGAOS void restart_amiwm() { flushmodules(); flushclients(); XFlush(dpy); XCloseDisplay(dpy); execvp(main_argv[0], main_argv); } #endif int handler(Display *d, XErrorEvent *e) { if (initting && (e->request_code == X_ChangeWindowAttributes) && (e->error_code == BadAccess)) { fprintf(stderr, "%s: Another window manager is already running. Not started.\n", progname); exit(1); } if (ignore_badwindow && (e->error_code == BadWindow || e->error_code == BadColor)) return 0; if ((e->error_code == BadMatch && e->request_code == X_ChangeSaveSet) || (e->error_code == BadWindow && e->request_code == X_ChangeProperty) || (e->error_code == BadWindow && e->request_code == X_GetProperty) || (e->error_code == BadWindow && e->request_code == X_GetWindowAttributes) || (e->error_code == BadWindow && e->request_code == X_ChangeWindowAttributes) || (e->error_code == BadDrawable && e->request_code == X_GetGeometry) || (e->error_code == BadWindow && e->request_code == X_SendEvent)) return 0; XmuPrintDefaultErrorMessage(d, e, stderr); if (initting) { fprintf(stderr, "%s: failure during initialisation; aborting\n", progname); exit(1); } return 0; } static struct coevent { struct coevent *next; struct timeval when; void (*what)(void *); void *with; } *eventlist=NULL; #define FIXUPTV(tv) { \ while((tv).tv_usec<0) { (tv).tv_usec+=1000000; (tv).tv_sec--; } \ while((tv).tv_usec>=1000000) { (tv).tv_usec-=1000000; (tv).tv_sec++; } \ } static void remove_call_out(void (*what)(void *), void *with) { struct coevent *ee, **e=&eventlist; while(*e && ((*e)->what != what || (*e)->with != with)) e=&(*e)->next; if((ee=*e)) { *e=(*e)->next; free(ee); } } #ifdef BSD_STYLE_GETTIMEOFDAY #define GETTIMEOFDAY(tp) gettimeofday(tp, NULL) #else #define GETTIMEOFDAY(tp) gettimeofday(tp) #endif static void call_out(int howlong_s, int howlong_u, void (*what)(void *), void *with) { struct coevent *ce=malloc(sizeof(struct coevent)); if(ce) { struct coevent **e=&eventlist; GETTIMEOFDAY(&ce->when); ce->when.tv_sec+=howlong_s; ce->when.tv_usec+=howlong_u; FIXUPTV(ce->when); ce->what=what; ce->with=with; while(*e && ((*e)->when.tv_secwhen.tv_sec || ((*e)->when.tv_sec==ce->when.tv_sec && (*e)->when.tv_usec<=ce->when.tv_usec))) e=&(*e)->next; ce->next=*e; *e=ce; } } static void call_call_out() { struct timeval now; struct coevent *e; GETTIMEOFDAY(&now); FIXUPTV(now); while((e=eventlist) && (e->when.tv_secwhen.tv_sec==now.tv_sec && e->when.tv_usec<=now.tv_usec))) { eventlist=e->next; (e->what)(e->with); free(e); } } static void fill_in_call_out(struct timeval *tv) { GETTIMEOFDAY(tv); tv->tv_sec=eventlist->when.tv_sec-tv->tv_sec; tv->tv_usec=eventlist->when.tv_usec-tv->tv_usec; FIXUPTV(*tv); if(tv->tv_sec<0) tv->tv_sec = tv->tv_usec = 0; } void add_fd_to_set(int fd) { FD_SET(fd, &master_fd_set); if(fd>=max_fd) max_fd=fd+1; } void remove_fd_from_set(int fd) { FD_CLR(fd, &master_fd_set); } static void lookup_keysyms(Display *dpy, unsigned int *meta_mask, unsigned int *switch_mask) { int i,j,k,maxsym,mincode,maxcode; XModifierKeymap *map=XGetModifierMapping(dpy); KeySym *kp, *kmap; unsigned int alt_mask = 0; *meta_mask=0, *switch_mask=0; XDisplayKeycodes(dpy, &mincode, &maxcode); kmap=XGetKeyboardMapping(dpy, mincode, maxcode-mincode+1, &maxsym); for(i=3; i<8; i++) for(j=0; jmax_keypermod; j++) if(map->modifiermap[i*map->max_keypermod+j] >= mincode) for(kp=kmap+(map->modifiermap[i*map->max_keypermod+j]-mincode)*maxsym, k=0; ktitle=s->deftitle; XClearWindow(dpy, s->menubar); redrawmenubar(s, s->menubar); if(free_screentitle) { free(free_screentitle); free_screentitle=NULL; } } void wberror(Scrn *s, char *message) { remove_call_out((void(*)(void *))restorescreentitle, s); (scr=s)->title=message; XClearWindow(dpy, s->menubar); redrawmenubar(s, s->menubar); XBell(dpy, 100); call_out(2, 0, (void(*)(void *))restorescreentitle, s); } void setfocus(Window w) { if(w == None && prefs.focus != FOC_CLICKTOTYPE) w = PointerRoot; XSetInputFocus(dpy, w, (prefs.focus==FOC_CLICKTOTYPE? RevertToNone:RevertToPointerRoot), CurrentTime); } static void update_clock(void *dontcare); static void grab_server() { if(!server_grabs++) XGrabServer(dpy); } static void ungrab_server() { if(!--server_grabs) { XUngrabServer(dpy); if(prefs.titlebarclock) { remove_call_out(update_clock, NULL); update_clock(NULL); } } } static void compress_motion(XEvent *event) { if (event->type != MotionNotify) return; while (XCheckTypedWindowEvent(dpy, event->xmotion.window, MotionNotify, event)) ; } static Bool grab_for_motion(Window w, Window confine, Cursor curs, Time time, int x_root, int y_root) { /* * To avoid misclicks, only grab for move/resize/etc, if the user has * moved the pointer by a given threshold. */ static const int THRESHOLD = 5; int status; XSync(dpy, False); status = XGrabPointer(dpy, w, False, ButtonPressMask|ButtonReleaseMask|Button1MotionMask, GrabModeAsync, GrabModeAsync, confine, None, time); if (status == AlreadyGrabbed || status == GrabSuccess) { for (;;) { XEvent event; int dx, dy; XMaskEvent(dpy, ButtonPressMask|ButtonReleaseMask|Button1MotionMask, &event); XPutBackEvent(dpy, &event); if (event.type != MotionNotify) break; compress_motion(&event); dx = x_root - event.xmotion.x_root; dy = y_root - event.xmotion.y_root; if (dx*dx + dy*dy > THRESHOLD*THRESHOLD) /* Pythagoras! */ return True; } } XUngrabPointer(dpy, CurrentTime); return False; } static void get_drag_event(XEvent *event) { static const unsigned int DRAG_MASK = ButtonPressMask|ButtonReleaseMask|Button1MotionMask; for (;;) { fd_set rfds = master_fd_set; struct timeval t = { .tv_sec = 0, .tv_usec = 0, }; while (XCheckMaskEvent(dpy, ExposureMask|DRAG_MASK, event)) { Client *c = NULL; Icon *i = NULL; Scrn *s = NULL; if (event->type == MotionNotify) compress_motion(event); if (event->type != Expose) return; if (event->xexpose.count) continue; else if (!XFindContext(dpy, event->xexpose.window, client_context, (XPointer*)&c)) redraw(c, event->xexpose.window); else if (!XFindContext(dpy, event->xexpose.window, icon_context, (XPointer*)&i)) redrawicon(i, event->xexpose.window); else if (!XFindContext(dpy, event->xexpose.window, screen_context, (XPointer*)&s)) redrawmenubar(s, event->xexpose.window); continue; } if (select(max_fd, &rfds, NULL, NULL, &t) > 0) { handle_module_input(&rfds); if(FD_ISSET(ConnectionNumber(dpy), &rfds)) XFlush(dpy); continue; } XFlush(dpy); rfds = master_fd_set; if(eventlist) fill_in_call_out(&t); if(select(max_fd, &rfds, NULL, NULL, (eventlist? &t:NULL))<0) { if (errno != EINTR) { perror("select"); break; } } else { call_call_out(); handle_module_input(&rfds); if(FD_ISSET(ConnectionNumber(dpy), &rfds)) XFlush(dpy); } } } struct bounding { Scrn *scr; int x, y, w, h; int d_offset; }; static void draw_bounding(struct bounding *bounding) { int x = bounding->x; int y = bounding->y; int w = bounding->w; int h = bounding->h; if (w < 0) { x += w; w *= -1; } if (h < 0) { y += h; h *= -1; } if (w >= HYSTERESIS || h >=HYSTERESIS) { XSetDashes(dpy, bounding->scr->rubbergc, bounding->d_offset, (char[]){6}, 1); XSetLineAttributes(dpy, bounding->scr->rubbergc, 0, LineOnOffDash, CapButt, JoinMiter); XDrawRectangle(dpy, bounding->scr->back, bounding->scr->rubbergc, x, y, w, h); XSetLineAttributes(dpy, bounding->scr->rubbergc, 0, LineSolid, CapButt, JoinMiter); } } static void move_dashes(void *ctx) { struct bounding *bounding = ctx; call_out(0, 50000, move_dashes, bounding); draw_bounding(bounding); if (--bounding->d_offset < 0) bounding->d_offset = 11; draw_bounding(bounding); } static void drag_bounding(Scrn *s, Time time, int x_root, int y_root, int x, int y) { struct bounding bounding = { .scr = s, .x = x, .y = y, .w = 0, .h = 0, .d_offset = 0, }; if (!grab_for_motion(s->back, None, None, time, x_root, y_root)) return; grab_server(); call_out(0, 0, move_dashes, &bounding); for (;;) { XEvent event; get_drag_event(&event); draw_bounding(&bounding); if (event.type == ButtonRelease && event.xbutton.button == Button1) { remove_call_out(move_dashes, &bounding); if (!(event.xbutton.state & ShiftMask)) deselect_all_icons(s); if (bounding.w < 0) { bounding.x += bounding.w; bounding.w *= -1; } if (bounding.w < 0) { bounding.y += bounding.h; bounding.h *= -1; } if (bounding.w >= HYSTERESIS || bounding.h >= HYSTERESIS) { for(Icon *i = s->icons; i != NULL; i = i->next) { int bx, by; if(i->window && i->mapped && XTranslateCoordinates(dpy, i->parent, s->back, i->x, i->y, &bx, &by, &(Window){0}) && bx < bounding.x + bounding.w && bx + i->width>bounding.x && by < bounding.y + bounding.h && by + i->height>bounding.y) { selecticon(i); } } } break; } if (event.type == ButtonPress && event.xbutton.button == Button3) break; if (event.type != MotionNotify) continue; bounding.w = event.xmotion.x_root - x_root; bounding.h = event.xmotion.y_root - y_root; draw_bounding(&bounding); } ungrab_server(); XUngrabPointer(dpy, CurrentTime); } static void drag_move(Client *c, Time time, int x_root, int y_root) { int x = c->x; int y = c->y; int dx = x_root - c->x; int dy = y_root - c->y; if (!grab_for_motion(c->drag, c->scr->back, None, time, x_root, y_root)) return; grab_server(); if(!prefs.opaquemove) { XDrawRectangle(dpy, c->scr->back, c->scr->rubbergc, x, y, c->pwidth-1, c->pheight-1); } for (;;) { XEvent event; get_drag_event(&event); if(!prefs.opaquemove) { XDrawRectangle(dpy, c->scr->back, c->scr->rubbergc, x, y, c->pwidth-1, c->pheight-1); } if (event.type == ButtonRelease && event.xbutton.button == Button1) { XMoveWindow(dpy, c->parent, c->x = x, c->y = y); break; } if (event.type == ButtonPress && event.xbutton.button == Button3) break; if (event.type != MotionNotify) continue; x = event.xmotion.x_root - dx; y = event.xmotion.y_root - dy; if(!forcemoving) { if (prefs.forcemove==FM_AUTO && (x + c->pwidth - c->scr->width > (c->pwidth>>2) || y + c->pheight - c->scr->height > (c->pheight>>2) || -x > (c->pwidth>>2) || -y > (c->pheight>>2))) forcemoving=1; else { if (x + c->pwidth > c->scr->width) x = c->scr->width-c->pwidth; if (y + c->pheight > c->scr->height) y = c->scr->height-c->pheight; if (x < 0) x = 0; if (y < 0) y = 0; } } if(prefs.opaquemove) { if (y <= -c->scr->bh) y = 1 - c->scr->bh; XMoveWindow(dpy, c->parent, c->x = x, c->y = y); } else { XDrawRectangle(dpy, c->scr->back, c->scr->rubbergc, x, y, c->pwidth-1, c->pheight-1); } } sendconfig(c); ungrab_server(); XUngrabPointer(dpy, CurrentTime); } static void drag_screen(Scrn *s, Time time, int x_root, int y_root) { int y = s->y; int dy = y_root - s->y; if (!grab_for_motion(s->menubar, None, None, time, x_root, y_root)) return; for (;;) { XEvent event; get_drag_event(&event); if (event.type == ButtonRelease && event.xbutton.button == Button1) { s->y = y; break; } if (event.type == ButtonPress && event.xbutton.button == Button3) break; if (event.type != MotionNotify) continue; y = event.xmotion.y_root - dy; #warning TODO: when add multihead, drag screen relative to monitor geometry if (y < 0) y = 0; if (y >= s->height) y = s->height - 1; XMoveWindow(dpy, s->back, -s->bw, y - s->bw); } XMoveWindow(dpy, s->back, -s->bw, s->y - s->bw); XUngrabPointer(dpy, CurrentTime); #ifndef ASSIMILATE_WINDOWS scrsendconfig(s); #endif } static void drag_icon(Scrn *s, Time time, int x_root, int y_root) { int nicons = 0; DragIcon *dragging = NULL; Icon *i; for (i = s->firstselected; i != NULL; i = i->nextselected) nicons++; if (nicons < 1) return; dragging = calloc(nicons, sizeof(*dragging)); if (dragging == NULL) { /* is XBell(3) the best way to signal error? */ XBell(dpy, 100); return; } if (!grab_for_motion(s->back, None, None, time, x_root, y_root)) { free(dragging); return; } for (nicons = 0, i = s->firstselected; i != NULL; i = i->nextselected, nicons++) { XWindowAttributes xwa; XSetWindowAttributes xswa; dragging[nicons].icon = i; XGetWindowAttributes(dpy, i->window, &xwa); XTranslateCoordinates(dpy, i->window, s->root, 0, 0, &dragging[nicons].x, &dragging[nicons].y, &(Window){0}); dragging[nicons].x += 4; dragging[nicons].y += 4; xswa.save_under = True; xswa.override_redirect = True; if (i->innerwin != None) { XGetWindowAttributes(dpy, i->innerwin, &xwa); xswa.background_pixmap = dragging[nicons].pm = XCreatePixmap( dpy, i->innerwin, xwa.width, xwa.height, xwa.depth ); XCopyArea(dpy, i->innerwin, dragging[nicons].pm, s->gc, 0, 0, xwa.width, xwa.height, 0, 0); } else { if (i->secondpm) { xswa.background_pixmap = i->secondpm; XGetGeometry(dpy, i->secondpm, &xwa.root, &(int){0}, &(int){0}, (unsigned int *)&xwa.width, (unsigned int *)&xwa.height, &(unsigned int){0}, (unsigned int *)&xwa.depth); } else if (i->iconpm) { xswa.background_pixmap = i->iconpm; XGetGeometry(dpy, i->iconpm, &xwa.root, &(int){0}, &(int){0}, (unsigned int *)&xwa.width, (unsigned int *)&xwa.height, &(unsigned int){0}, (unsigned int *)&xwa.depth); } if (xwa.depth != s->depth) { dragging[nicons].pm = XCreatePixmap(dpy, i->window, xwa.width, xwa.height, xwa.depth); XSetForeground(dpy, s->gc, s->dri.dri_Pens[SHADOWPEN]); XSetBackground(dpy, s->gc, s->dri.dri_Pens[BACKGROUNDPEN]); XCopyPlane(dpy, xswa.background_pixmap, dragging[nicons].pm, s->gc, 0, 0, xwa.width, xwa.height, 0, 0, 1); xswa.background_pixmap = dragging[nicons].pm; xwa.depth = s->depth; } } xswa.colormap = xwa.colormap; dragging[nicons].w = XCreateWindow( dpy, s->root, dragging[nicons].x, dragging[nicons].y, xwa.width, xwa.height, 0, xwa.depth, xwa.class, xwa.visual, CWBackPixmap|CWOverrideRedirect|CWSaveUnder|CWColormap, &xswa ); #ifdef HAVE_XSHAPE if (shape_extn) { if (i->innerwin != None) { int b_shaped; XShapeQueryExtents( dpy, i->innerwin, &b_shaped, &(int){0}, &(int){0}, &(unsigned){0}, &(unsigned){0}, &(int){0}, &(int){0}, &(int){0}, &(unsigned){0}, &(unsigned){0} ); if (b_shaped) XShapeCombineShape( dpy, dragging[nicons].w, ShapeBounding, 0, 0, i->innerwin, ShapeBounding, ShapeSet ); } else if (i->maskpm != None) { XShapeCombineMask( dpy, dragging[nicons].w, ShapeBounding, 0, 0, i->maskpm, ShapeSet ); } } #endif XMapRaised(dpy, dragging[nicons].w); } for (;;) { XEvent event; get_drag_event(&event); if (event.type == ButtonRelease && event.xbutton.button == Button1) { Scrn *s = get_front_scr(); int wx, wy; Window ch; Client *c; for (;;) { if (s->root == event.xbutton.root && event.xbutton.y_root >= s->y) break; s = s->behind; if (s == get_front_scr()) { goto error; } } if (s->deftitle == NULL) goto error; if (XTranslateCoordinates(dpy, s->root, s->back, x_root, y_root, &wx, &wy, &ch) && ch != None) { if (XFindContext(dpy, ch, client_context, (void *)&c) || c->scr != s || c->state != NormalState) { c = NULL; } } else { c = NULL; } if (c != NULL) { if (c->module != NULL) { XTranslateCoordinates(dpy, s->back, c->window, -4, -4, &wx, &wy, &ch); for (int n = 0; n < nicons; n++) { dispatch_event_to_broker(mkcmessage( c->window, ATOMS[AMIWM_APPWINDOWMSG],dragging[n].icon->window, dragging[n].x + wx, dragging[n].y + wy ), 0, c->module); } goto done; } else { goto error; } } for (int n = 0; n < nicons; n++) { if (dragging[n].icon->scr != s) { dragging[n].icon->mapped = False; /* reparenticon() needs it false */ reparenticon(dragging[n].icon, s, dragging[n].x - 4, dragging[n].y - 4 - s->y); } else { XMoveWindow(dpy, dragging[n].icon->window, dragging[n].icon->x = dragging[n].x - 4, dragging[n].icon->y = dragging[n].y - 4 - s->y); } dragging[n].icon->mapped = True; adjusticon(dragging[n].icon); } goto done; } if (event.type == ButtonPress && event.xbutton.button == Button3) break; if (event.type != MotionNotify) continue; for (int n = 0; n < nicons; n++) { int dx = x_root - dragging[n].x; int dy = y_root - dragging[n].y; XMoveWindow(dpy, dragging[n].w, dragging[n].x = event.xmotion.x_root - dx, dragging[n].y = event.xmotion.y_root - dy); } x_root = event.xmotion.x_root; y_root = event.xmotion.y_root; } error: wberror(dragging[0].icon->scr, "Icon can't be moved into this window"); done: for (int n = 0; n < nicons; n++) { XDestroyWindow(dpy, dragging[n].w); if(dragging[n].pm) XFreePixmap(dpy, dragging[n].pm); } free(dragging); XUngrabPointer(dpy, CurrentTime); } static void drag_resize(Client *c, Time time, int x_root, int y_root) { int w = c->pwidth; int h = c->pheight; int dx = w - (x_root - c->x); int dy = h - (y_root - c->y); if (!grab_for_motion(c->drag, c->scr->back, None, time, x_root, y_root)) return; grab_server(); if(!prefs.opaqueresize) XDrawRectangle(dpy, c->scr->back, c->scr->rubbergc, c->x, c->y, w-1, h-1); for (;;) { XEvent event; get_drag_event(&event); if(!prefs.opaqueresize) XDrawRectangle(dpy, c->scr->back, c->scr->rubbergc, c->x, c->y, w-1, h-1); if (event.type == ButtonRelease && event.xbutton.button == Button1) { resizeclientwindow(c, w, h); break; } if (event.type == ButtonPress && event.xbutton.button == Button3) break; if (event.type != MotionNotify) continue; w = event.xmotion.x_root - c->x + dx; h = event.xmotion.y_root - c->y + dy; if (c->sizehints->width_inc) { w -= c->sizehints->base_width + c->framewidth; w -= w % c->sizehints->width_inc; w += c->sizehints->base_width; if (w > c->sizehints->max_width) w = c->sizehints->max_width; if (w < c->sizehints->min_width) w = c->sizehints->min_width; w += c->framewidth; } if (c->sizehints->height_inc) { h -= c->sizehints->base_height + c->frameheight; h -= h % c->sizehints->height_inc; h += c->sizehints->base_height; if (h > c->sizehints->max_height) h = c->sizehints->max_height; if (h < c->sizehints->min_height) h = c->sizehints->min_height; h += c->frameheight; } if(prefs.opaqueresize) { resizeclientwindow(c, w, h); } else { XDrawRectangle(dpy, c->scr->back, c->scr->rubbergc, c->x, c->y, w-1, h-1); } } sendconfig(c); ungrab_server(); XUngrabPointer(dpy, CurrentTime); } static void do_icon_double_click(Scrn *scr) { Icon *i, *next; for(i=scr->firstselected; i; i=next) { next=i->nextselected; if(i->module) { dispatch_event_to_broker(mkcmessage(i->window, ATOMS[AMIWM_APPICONMSG], 0), 0, i->module); } else { deiconify(i); } } } static void abortfocus() { if(activeclient) { activeclient->active=False; redrawclient(activeclient); if(prefs.focus==FOC_CLICKTOTYPE) XGrabButton(dpy, Button1, AnyModifier, activeclient->parent, True, ButtonPressMask, GrabModeSync, GrabModeAsync, None, wm_curs); activeclient = NULL; } setfocus(None); } static RETSIGTYPE sighandler(int sig) { signalled=1; signal(sig, SIG_IGN); } static void instcmap(Colormap c) { XInstallColormap(dpy, (c == None) ? scr->cmap : c); } void internal_broker(XEvent *e) { /* * XXX this is one of the things that the code in module.c * is abusing to overload display with some numeric * values. Surely there's a better way to do this? */ uintptr_t event_loc=(uintptr_t)e->xany.display; e->xany.display=dpy; if(event_loc==1) { XSendEvent(dpy, e->xany.window, False, 0, e); } else switch(e->type) { case MappingNotify: if(e->xmapping.request==MappingKeyboard || e->xmapping.request==MappingModifier) XRefreshKeyboardMapping(&e->xmapping); lookup_keysyms(dpy, &meta_mask, &switch_mask); break; case KeyPress: if(e->xkey.state & meta_mask) { KeySym ks=XLookupKeysym(&e->xkey, ((e->xkey.state & ShiftMask)?1:0)+ ((e->xkey.state & switch_mask)?2:0)); void *item; if((item=getitembyhotkey(ks))) menuaction(item); } break; } } static void update_clock(void *dontcare) { Scrn *scr; if(server_grabs) return; call_out(prefs.titleclockinterval, 0, update_clock, dontcare); scr = get_front_scr(); do { redrawmenubar(scr, scr->menubar); scr=scr->behind; } while(scr != get_front_scr()); } static void cleanup() { int sc; extern void free_prefs(); struct coevent *e; flushmodules(); flushclients(); scr = get_front_scr(); for(sc=0; checkwins!=NULL && scnext; free(e); } if(x_server) free(x_server); } int main(int argc, char *argv[]) { int x_fd, sc; static Argtype array[3]; struct RDArgs *ra; #ifdef USE_FONTSETS setlocale(LC_CTYPE, ""); setlocale(LC_TIME, ""); #endif main_argv=argv; progname=argv[0]; atexit(cleanup); memset(array, 0, sizeof(array)); initargs(argc, argv); if(!(ra=ReadArgs((UBYTE *)"RCFILE,DISPLAY/K,SINGLE/S", (LONG *)array, NULL))) { PrintFault(IoErr(), (UBYTE *)progname); exit(1); } x_server = strdup(XDisplayName(array[1].ptr)); XrmInitialize(); if(!(dpy = XOpenDisplay(array[1].ptr))) { fprintf(stderr, "%s: cannot connect to X server %s\n", progname, x_server); FreeArgs(ra); exit(1); } if(array[1].ptr) { char *env=malloc(strlen((char *)array[1].ptr)+10); sprintf(env, "DISPLAY=%s", (char *)array[1].ptr); putenv(env); } client_context = XUniqueContext(); screen_context = XUniqueContext(); icon_context = XUniqueContext(); menu_context = XUniqueContext(); vroot_context = XUniqueContext(); wm_curs=XCreateFontCursor(dpy, XC_top_left_arrow); FD_ZERO(&master_fd_set); x_fd=ConnectionNumber(dpy); FD_SET(x_fd, &master_fd_set); max_fd=x_fd+1; initting = 1; XSetErrorHandler(handler); #ifdef HAVE_XSHAPE if(XShapeQueryExtension(dpy, &shape_event_base, &shape_error_base)) shape_extn = 1; #endif XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); XSync(dpy, False); XSelectInput(dpy, DefaultRootWindow(dpy), NoEventMask); init_modules(); read_rc_file(array[0].ptr, !array[2].num); if( prefs.titleclockinterval < 1 ) prefs.titleclockinterval = 1; FreeArgs(ra); if (signal(SIGTERM, sighandler) == SIG_IGN) signal(SIGTERM, SIG_IGN); if (signal(SIGINT, sighandler) == SIG_IGN) signal(SIGINT, SIG_IGN); #ifdef SIGHUP if (signal(SIGHUP, sighandler) == SIG_IGN) signal(SIGHUP, SIG_IGN); #endif init_atoms(); #ifndef AMIGAOS if((fcntl(ConnectionNumber(dpy), F_SETFD, 1)) == -1) fprintf(stderr, "%s: child cannot disinherit TCP fd\n", progname); #endif lookup_keysyms(dpy, &meta_mask, &switch_mask); checkwins = calloc(ScreenCount(dpy), sizeof(*checkwins)); for(sc=0; scroot, checkwins[sc]); if(!getscreenbyroot(root)) { char buf[64]; sprintf(buf, "Screen.%d", sc); openscreen((sc? strdup(buf):"Workbench Screen"), root); } } } /* if(!front) openscreen("Workbench Screen", DefaultRootWindow(dpy)); */ realizescreens(); setfocus(None); initting = 0; if(prefs.titlebarclock) call_out(0, 0, update_clock, NULL); while(!signalled) { fd_set rfds; struct timeval t; XEvent event; Window dummy_root; int dummy_x, dummy_y; unsigned int dummy_w, dummy_h, dummy_bw, dummy_d; while((!signalled) && QLength(dpy)>0) { Client *c; Icon *i; XNextEvent(dpy, &event); if(!XFindContext(dpy, event.xany.window, client_context, (XPointer*)&c)) { scr=c->scr; } else { c = NULL; if(XFindContext(dpy, event.xany.window, screen_context, (XPointer*)&scr)) scr = get_front_scr(); } if(XFindContext(dpy, event.xany.window, icon_context, (XPointer*)&i)) i=NULL; else scr=i->scr; switch(event.type) { case Expose: if(!event.xexpose.count) { if(c) redraw(c, event.xexpose.window); else if(i) redrawicon(i, event.xexpose.window); else if(scr) redrawmenubar(scr, event.xexpose.window); } break; case CreateNotify: if(!XFindContext(dpy, event.xcreatewindow.window, client_context, (XPointer *)&c)) { break; } if(!event.xcreatewindow.override_redirect) { if(!(scr=getscreenbyroot(event.xcreatewindow.parent))) scr = get_front_scr(); createclient(event.xcreatewindow.window); } #ifdef ASSIMILATE_WINDOWS else if(XFindContext(dpy, event.xcreatewindow.window, screen_context, (XPointer*)&scr) && (scr=getscreenbyroot(event.xcreatewindow.parent))) { XGetWindowAttributes(dpy, event.xcreatewindow.window, &attr); assimilate(event.xcreatewindow.window, attr.x, attr.y); } #endif break; case DestroyNotify: if(!XFindContext(dpy, event.xdestroywindow.window, client_context, (XPointer*)&c)) { ignore_badwindow = 1; rmclient(c); XSync(dpy, False); ignore_badwindow = 0; } else if(!XFindContext(dpy, event.xdestroywindow.window, icon_context, (XPointer*)&i)) { ignore_badwindow = 1; if(i->client) i->client->icon=NULL; rmicon(i); XSync(dpy, False); ignore_badwindow = 0; } else if(event.xdestroywindow.window) XDeleteContext(dpy, event.xdestroywindow.window, screen_context); break; case UnmapNotify: if(c && c->active && (event.xunmap.window==c->parent) && !(c->fullscreen && c->state == NormalState)) { c->active=False; activeclient = NULL; redrawclient(c); if(prefs.focus == FOC_CLICKTOTYPE) XGrabButton(dpy, Button1, AnyModifier, c->parent, True, ButtonPressMask, GrabModeSync, GrabModeAsync, None, wm_curs); if(!menuactive) setfocus(None); } if(c && (event.xunmap.window==c->window)) { if((!c->reparenting) && c->parent != c->scr->root) { Icon *i=c->icon; XUnmapWindow(dpy, c->parent); if(i) { if(i->labelwin) XUnmapWindow(dpy, i->labelwin); if(i->window) XUnmapWindow(dpy, i->window); i->mapped=0; deselecticon(i); } setclientstate(c, WithdrawnState); } c->reparenting = 0; } break; case ConfigureNotify: if((!XFindContext(dpy, event.xconfigure.window, icon_context, (XPointer *)&i)) && event.xconfigure.window == i->window){ i->x=event.xconfigure.x; i->y=event.xconfigure.y; i->width=event.xconfigure.width; i->height=event.xconfigure.height; if(i->labelwin) { XWindowChanges xwc; xwc.x=i->x+(i->width>>1)-(i->labelwidth>>1); xwc.y=i->y+i->height+1; xwc.sibling=i->window; xwc.stack_mode=Below; XConfigureWindow(dpy, i->labelwin, CWX|CWY|CWSibling|CWStackMode, &xwc); } } break; case ReparentNotify: if((!XFindContext(dpy, event.xreparent.window, icon_context, (XPointer *)&i)) && event.xreparent.window == i->window){ i->parent=event.xreparent.parent; i->x=event.xreparent.x; i->y=event.xreparent.y; if(i->labelwin) { XWindowChanges xwc; XReparentWindow(dpy, i->labelwin, i->parent, i->x+(i->width>>1)-(i->labelwidth>>1), i->y+i->height+1); xwc.sibling=i->window; xwc.stack_mode=Below; XConfigureWindow(dpy, i->labelwin, CWSibling|CWStackMode, &xwc); } } break; case CirculateNotify: case GravityNotify: case NoExpose: case GraphicsExpose: break; case ClientMessage: if(c) handle_client_message(c, &event.xclient); break; case ColormapNotify: if(event.xcolormap.new && c) if(c->colormap!=event.xcolormap.colormap) { c->colormap=event.xcolormap.colormap; if(c->active) instcmap(c->colormap); } break; case ConfigureRequest: if(XFindContext(dpy, event.xconfigurerequest.window, client_context, (XPointer*)&c)) c = NULL; if(c && event.xconfigurerequest.window==c->window && c->parent!=c->scr->root) { extern void resizeclientwindow(Client *c, int, int); if(event.xconfigurerequest.value_mask&CWBorderWidth) c->old_bw=event.xconfigurerequest.border_width; resizeclientwindow(c, (event.xconfigurerequest.value_mask&CWWidth)? event.xconfigurerequest.width+c->framewidth:c->pwidth, (event.xconfigurerequest.value_mask&CWHeight)? event.xconfigurerequest.height+c->frameheight:c->pheight); if((event.xconfigurerequest.value_mask&(CWX|CWY)) && c->state==WithdrawnState) XMoveWindow(dpy, c->parent, c->x=((event.xconfigurerequest.value_mask&CWX)? event.xconfigurerequest.x:c->x), c->y=((event.xconfigurerequest.value_mask&CWY)? event.xconfigurerequest.y:c->y)); } else { if(!XFindContext(dpy, event.xconfigurerequest.window, screen_context, (XPointer *)&scr)) if((event.xconfigurerequest.y-=scr->y)<0) event.xconfigurerequest.y=0; XConfigureWindow(dpy, event.xconfigurerequest.window, event.xconfigurerequest.value_mask, (XWindowChanges *)&event.xconfigurerequest.x); } break; case CirculateRequest: if(XFindContext(dpy, event.xcirculaterequest.window, client_context, (XPointer*)&c)) if(event.xcirculaterequest.place==PlaceOnTop) XRaiseWindow(dpy, event.xcirculaterequest.window); else { Client *c2; Window r,p,*children; unsigned int nchildren; if(XQueryTree(dpy, scr->back, &r, &p, &children, &nchildren)) { int n; for(n=0; nparent) break; if(nparent==c->scr->root && (xwmh=XGetWMHints(dpy, c->window))) { if(c->state==WithdrawnState && (xwmh->flags&StateHint) && xwmh->initial_state==IconicState) c->state=IconicState; XFree(xwmh); } switch(c->state) { case WithdrawnState: if(c->parent == c->scr->root) reparent(c); case NormalState: XMapWindow(dpy, c->window); if (!c->fullscreen) XMapRaised(dpy, c->parent); setclientstate(c, NormalState); break; case IconicState: if(c->parent == c->scr->root) reparent(c); iconify(c); break; } } break; case MapNotify: if(prefs.focus == FOC_CLICKTOTYPE && c && event.xmap.window == c->parent && c->parent != c->scr->root && (!c->active)) { if(activeclient) { XGrabButton(dpy, Button1, AnyModifier, activeclient->parent, True, ButtonPressMask, GrabModeSync, GrabModeAsync, None, wm_curs); activeclient->active=False; redrawclient(activeclient); } c->active=True; activeclient = c; XUngrabButton(dpy, Button1, AnyModifier, c->parent); redrawclient(c); setfocus(c->window); } break; case EnterNotify: if(menuactive) { scr=menuactive; menubar_enter(event.xcrossing.window); } else if(clickwindow && event.xcrossing.window == clickwindow) clickenter(); else if(c) { if((!c->active) && (c->state==NormalState) && prefs.focus!=FOC_CLICKTOTYPE) { if(activeclient) { activeclient->active=False; redrawclient(activeclient); } setfocus(c->window); c->active=True; activeclient = c; redrawclient(c); if(prefs.autoraise && c->parent!=c->scr->root) XRaiseWindow(dpy, c->parent); } if(event.xcrossing.window==c->window) instcmap(c->colormap); } break; case LeaveNotify: if(menuactive) { scr=menuactive; menubar_leave(event.xcrossing.window); } else if(clickwindow && event.xcrossing.window == clickwindow) clickleave(); else if(c) { if(c->active && event.xcrossing.window==c->parent && event.xcrossing.detail!=NotifyInferior && prefs.focus == FOC_FOLLOWMOUSE) { if(!menuactive) setfocus(None); c->active=False; activeclient = NULL; instcmap(None); redrawclient(c); } else if(event.xcrossing.window==c->window && event.xcrossing.detail!=NotifyInferior && event.xcrossing.mode==NotifyNormal) instcmap(None); } break; case ButtonPress: if(clickwindow==None && event.xbutton.button==Button1 && !menuactive) { if(c) { if((!c->active) && prefs.focus==FOC_CLICKTOTYPE && (c->state==NormalState)) { if(activeclient) { activeclient->active=False; redrawclient(activeclient); XGrabButton(dpy, Button1, AnyModifier, activeclient->parent, True, ButtonPressMask, GrabModeSync, GrabModeAsync, None, wm_curs); } setfocus(c->window); c->active=True; activeclient = c; redrawclient(c); XUngrabButton(dpy, Button1, AnyModifier, c->parent); if(prefs.autoraise && c->parent!=c->scr->root) XRaiseWindow(dpy, c->parent); } if(event.xbutton.window!=c->depth && event.xbutton.window!=c->window) { if(c==doubleclient && (event.xbutton.time-last_double)< dblClickTime) { XRaiseWindow(dpy, c->parent); } else { doubleclient=c; last_double=event.xbutton.time; } } if(event.xbutton.window==c->drag) { forcemoving=(prefs.forcemove==FM_ALWAYS) || (event.xbutton.state & ShiftMask); drag_move(c, event.xbutton.time, event.xbutton.x_root, event.xbutton.y_root); } else if(event.xbutton.window==c->resize) drag_resize(c, event.xbutton.time, event.xbutton.x_root, event.xbutton.y_root); else if(event.xbutton.window==c->window || event.xbutton.window==c->parent) ; else gadgetclicked(c, event.xbutton.window, &event); } else if(i && event.xbutton.window==i->window) { abortfocus(); if(i->selected && (event.xbutton.time-last_icon_click)scr); } else { if(!(event.xbutton.state & ShiftMask)) deselect_all_icons(i->scr); last_icon_click=event.xbutton.time; selecticon(i); drag_icon(i->scr, event.xbutton.time, event.xbutton.x_root, event.xbutton.y_root); } } else if(scr&&event.xbutton.window==scr->menubardepth) { clickwindow=scr->menubardepth; mbdclick=mbdscr=scr; redrawmenubar(scr, scr->menubardepth); } else if(scr&&event.xbutton.window==scr->menubar && scr->back!=scr->root) { drag_screen(scr, event.xbutton.time, event.xbutton.x_root, event.xbutton.y_root); } else if(scr&&scr->back==event.xbutton.window) { abortfocus(); drag_bounding(scr, event.xbutton.time, event.xbutton.x_root, event.xbutton.y_root, event.xbutton.x, event.xbutton.y); } else ; } else if(event.xbutton.button==3) { if(scr&&(scr==mbdscr)&&clickwindow==scr->menubardepth) { mbdclick=NULL; clickwindow=None; redrawmenubar(scr, scr->menubardepth); } else if(clickclient) { gadgetaborted(clickclient); } else if(scr&&!menuactive) { menu_on(); menuactive=scr; } } if(prefs.focus == FOC_CLICKTOTYPE && !menuactive) { XSync(dpy,0); XAllowEvents(dpy,ReplayPointer,CurrentTime); XSync(dpy,0); } break; case ButtonRelease: if(event.xbutton.button==Button1) { if(clickclient) gadgetunclicked(clickclient, &event); else if((scr=mbdscr)&& clickwindow==scr->menubardepth) { if(mbdclick) { mbdclick=NULL; redrawmenubar(scr, scr->menubardepth); screentoback(); } clickwindow=None; } } else if(event.xbutton.button==Button3 && (scr=menuactive)) { menu_off(); menuactive=NULL; } break; case MotionNotify: break; case KeyPress: if(!dispatch_event_to_broker(&event, KeyPressMask, modules)) internal_broker(&event); break; case KeyRelease: if(!dispatch_event_to_broker(&event, KeyPressMask, modules)) internal_broker(&event); break; case MappingNotify: if(!dispatch_event_to_broker(&event, 0, modules)) internal_broker(&event); break; case PropertyNotify: if(event.xproperty.atom != None && c && event.xproperty.window==c->window && XGetGeometry(dpy, c->window, &dummy_root, &dummy_x, &dummy_y, &dummy_w, &dummy_h, &dummy_bw, &dummy_d)) propertychange(c, event.xproperty.atom); break; case FocusOut: /* Ignore */ break; case FocusIn: if(event.xfocus.detail == NotifyDetailNone && prefs.focus == FOC_CLICKTOTYPE && (scr = getscreenbyroot(event.xfocus.window))) { Window w; int rt; XGetInputFocus(dpy, &w, &rt); if(w == None) setfocus(scr->inputbox); } break; default: #ifdef HAVE_XSHAPE if(shape_extn && event.type == shape_event_base + ShapeNotify) { XShapeEvent *s = (XShapeEvent *) &event; if(c && s->kind == ShapeBounding) { c->shaped = s->shaped; reshape_frame(c); } break; } #endif fprintf(stderr, "%s: got unexpected event type %d.\n", progname, event.type); } } if(signalled) break; rfds = master_fd_set; t.tv_sec = t.tv_usec = 0; if (select(max_fd, &rfds, NULL, NULL, &t) > 0) { handle_module_input(&rfds); if(FD_ISSET(x_fd, &rfds)) XPeekEvent(dpy, &event); continue; } if(signalled) break; XFlush(dpy); rfds = master_fd_set; if(eventlist) fill_in_call_out(&t); if(select(max_fd, &rfds, NULL, NULL, (eventlist? &t:NULL))<0) { if (errno != EINTR) { perror("select"); break; } } else { call_call_out(); handle_module_input(&rfds); if(FD_ISSET(x_fd, &rfds)) XPeekEvent(dpy, &event); } } if(prefs.titlebarclock) remove_call_out(update_clock, NULL); if(signalled) fprintf(stderr, "%s: exiting on signal\n", progname); exit(signalled? 0:1); }