From 1212e7a78c967f0d9a3d53948f825ce35a153088 Mon Sep 17 00:00:00 2001 From: Lucas de Sena Date: Fri, 30 Jan 2026 01:54:19 +0000 Subject: [PATCH 1/3] document OpaqueResize/OpaqueMove --- amiwm.1 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/amiwm.1 b/amiwm.1 index 92e1561..85b00a3 100644 --- a/amiwm.1 +++ b/amiwm.1 @@ -113,6 +113,8 @@ The options are as follows: .It Ic InterScreenGap Ta 0 Ta Ar number .It Ic Module Ta "" Ta Qo Ar name Qc Op Qq initstring .It Ic ModulePath Ta "" Ta Qq Ar path Ns Op Cm :\& Ns Ar path ... +.It Ic OpaqueMove Ta False Ta Ar boolean +.It Ic OpaqueResize Ta False Ta Ar boolean .It Ic Screen Ta "" Ta Oo Ar number Oc Ar screenname .It Ic ScreenFont Ta "" Ta Qq Ar fontname .It Ic ShortLabelIcons Ta Cm off Ta Ar boolean @@ -183,6 +185,14 @@ This is the original AmigaOS policy. Allows you to move windows outside the perimeter of the root window when pressing shift, when trying to drag at least 25% of the window ofscreen, or always, respectively. +. +.It Ic OpaqueMove Ar bool +Whether moving a window by dragging its titlebar should move the window in real time; +or just move a frame indicating where the window will be after releasing the mouse button. +. +.It Ic OpaqueResize Ar bool +Whether resizing a window by dragging its resize handle should resize the window in real time; +or just resize a frame indicating the window's final size after releasing the mouse button. .El . .Ss Windows decorations From 5bbae05fece4cdeb96e6f085f97638420ad51d87 Mon Sep 17 00:00:00 2001 From: Lucas de Sena Date: Sat, 31 Jan 2026 01:25:50 +0000 Subject: [PATCH 2/3] icc: use compound literal rather than static object to return pointer thus make mkcmessage reentrant (and a macro, rather than a function) --- icc.c | 14 -------------- icc.h | 9 +++++++++ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/icc.c b/icc.c index aeb739e..23babe6 100644 --- a/icc.c +++ b/icc.c @@ -78,20 +78,6 @@ void setstringprop(Window w, Atom a, char *str) XSetTextProperty(dpy, w, &txtp, a); } -XEvent *mkcmessage(Window w, Atom a, long x) -{ - static XEvent ev; - - memset(&ev, 0, sizeof(ev)); - ev.xclient.type = ClientMessage; - ev.xclient.window = w; - ev.xclient.message_type = a; - ev.xclient.format = 32; - ev.xclient.data.l[0] = x; - ev.xclient.data.l[1] = CurrentTime; - return &ev; -} - void getwmstate(Client *c) { Atom *p; diff --git a/icc.h b/icc.h index 227e4dc..20d09b2 100644 --- a/icc.h +++ b/icc.h @@ -24,4 +24,13 @@ extern Atom wm_state, wm_change_state, wm_protocols, wm_delete, wm_take_focus, w #define Psizeright 8 #define Psizetrans 16 + +#define mkcmessage(w, a, x, ...) (&(XEvent){.xclient = { \ + .type = ClientMessage, \ + .window = (w), \ + .message_type = (a), \ + .format = 32, \ + .data.l = {(long)(x), CurrentTime, __VA_ARGS__}, \ +}}) + #endif From 8a59e5cc9e652beb469b3745c5e36becf0902646 Mon Sep 17 00:00:00 2001 From: Lucas de Sena Date: Sat, 31 Jan 2026 01:51:39 +0000 Subject: [PATCH 3/3] localize control flow and variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit State and control flow for modal mouse actions (dragging/resizing/etc) was fragmented and scattered through main.c, making code convoluted and hard to maintain. I fixed that by applying good structured programming practics: implementing sub-event-loops for those modal actions, rather than checking which modal action is currently in action at the main event-loop. That commit took me more time than i expected... abstract out all spaghettish, global-state control flow into a single, encapsulated scope for each modal action… test if i broke anything… find any dead code that is not used anymore (implementing that global state we had before)… I also put some `static` in global variables and routines, so the compile can warn whether an unused variable/function is defined (and help me on removing dead code). --- main.c | 1072 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 538 insertions(+), 534 deletions(-) diff --git a/main.c b/main.c index 4777704..eb58d60 100644 --- a/main.c +++ b/main.c @@ -72,32 +72,28 @@ typedef struct _DragIcon { } 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; -int signalled=0, forcemoving=0; -Client *activeclient=NULL, *dragclient=NULL, *resizeclient=NULL; -Client *rubberclient=NULL, *clickclient=NULL, *doubleclient=NULL; -Scrn *boundingscr=NULL; Window boundingwin=None; -DragIcon *dragiconlist=NULL; -int numdragicons=0; -Window clickwindow=None; -int rubberx, rubbery, rubberh, rubberw, rubberx0, rubbery0, olddragx, olddragy; -Time last_icon_click=0, last_double=0; -int initting=0; + +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; -int dblClickTime=1500; -XContext client_context, screen_context, icon_context, menu_context, vroot_context; -Scrn *dragscreen=NULL, *menuactive=NULL; -static int d_offset=0; +static int dblClickTime=1500; static fd_set master_fd_set; static int max_fd=0; static Window *checkwins; -char *free_screentitle=NULL; -char *x_server=NULL; -int shape_event_base, shape_error_base, shape_extn=0; -int server_grabs=0; - -unsigned int meta_mask, switch_mask; +static int shape_event_base, shape_error_base; +static int server_grabs=0; +static unsigned int meta_mask, switch_mask; static char **main_argv; @@ -107,6 +103,7 @@ 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); @@ -125,7 +122,6 @@ 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 XEvent *mkcmessage(Window w, Atom a, long x); extern void reshape_frame(Client *c); extern void read_rc_file(char *filename, int manage_all); extern void init_modules(); @@ -186,7 +182,7 @@ static struct coevent { while((tv).tv_usec>=1000000) { (tv).tv_usec-=1000000; (tv).tv_sec++; } \ } -void remove_call_out(void (*what)(void *), void *with) +static void remove_call_out(void (*what)(void *), void *with) { struct coevent *ee, **e=&eventlist; @@ -204,7 +200,7 @@ void remove_call_out(void (*what)(void *), void *with) #define GETTIMEOFDAY(tp) gettimeofday(tp) #endif -void call_out(int howlong_s, int howlong_u, void (*what)(void *), void *with) +static void call_out(int howlong_s, int howlong_u, void (*what)(void *), void *with) { struct coevent *ce=malloc(sizeof(struct coevent)); if(ce) { @@ -261,7 +257,7 @@ void remove_fd_from_set(int fd) FD_CLR(fd, &master_fd_set); } -void lookup_keysyms(Display *dpy, unsigned int *meta_mask, +static void lookup_keysyms(Display *dpy, unsigned int *meta_mask, unsigned int *switch_mask) { int i,j,k,maxsym,mincode,maxcode; @@ -297,7 +293,7 @@ void lookup_keysyms(Display *dpy, unsigned int *meta_mask, } -void restorescreentitle(Scrn *s) +static void restorescreentitle(Scrn *s) { (scr=s)->title=s->deftitle; XClearWindow(dpy, s->menubar); @@ -327,13 +323,13 @@ void setfocus(Window w) static void update_clock(void *dontcare); -void grab_server() +static void grab_server() { if(!server_grabs++) XGrabServer(dpy); } -void ungrab_server() +static void ungrab_server() { if(!--server_grabs) { XUngrabServer(dpy); @@ -344,389 +340,543 @@ void ungrab_server() } } -void drawrubber() +static void compress_motion(XEvent *event) { - if(rubberclient) - XDrawRectangle(dpy, rubberclient->scr->back, rubberclient->scr->rubbergc, - rubberx, rubbery, rubberw-1, rubberh-1); - else if(boundingwin) { - const char dash_list[] = { 6 }; - int x=rubberx, y=rubbery, w=rubberw, h=rubberh; - if(w<0) { x+=w; w=-w; } - if(h<0) { y+=h; h=-h; } - if(w>=HYSTERESIS || h>=HYSTERESIS) { - XSetDashes(dpy, boundingscr->rubbergc, d_offset, - dash_list, sizeof(dash_list)); - XSetLineAttributes(dpy, boundingscr->rubbergc, 0, LineOnOffDash, - CapButt, JoinMiter); - XDrawRectangle(dpy, boundingwin, boundingscr->rubbergc, x, y, w, h); - XSetLineAttributes(dpy, boundingscr->rubbergc, 0, LineSolid, - CapButt, JoinMiter); + 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 move_dashes(void *dontcare) +static void get_drag_event(XEvent *event) { - call_out(0, 50000, move_dashes, dontcare); - drawrubber(); - if((--d_offset)<0) - d_offset=11; - drawrubber(); -} + static const unsigned int DRAG_MASK = ButtonPressMask|ButtonReleaseMask|Button1MotionMask; -void endrubber() -{ - if(rubberclient) { - if((!prefs.opaquemove||dragclient==NULL) && - (!prefs.opaqueresize||resizeclient==NULL)) - drawrubber(); - rubberclient=NULL; - } else if(boundingwin) { - if((!prefs.opaquemove||dragclient==NULL) && - (!prefs.opaqueresize||resizeclient==NULL)) - drawrubber(); - boundingwin=None; - } -} + for (;;) { + fd_set rfds = master_fd_set; + struct timeval t = { + .tv_sec = 0, + .tv_usec = 0, + }; -void initrubber(int x0, int y0, Client *c) -{ - endrubber(); - rubberx=c->x; - rubbery=c->y; - rubberw=c->pwidth; - rubberh=c->pheight; - rubberx0=x0; - rubbery0=y0; - rubberclient=c; -} + while (XCheckMaskEvent(dpy, ExposureMask|DRAG_MASK, event)) { + Client *c = NULL; + Icon *i = NULL; + Scrn *s = NULL; -void abortrubber() -{ - if(rubberclient) { - endrubber(); - dragclient=resizeclient=NULL; - ungrab_server(); - XUngrabPointer(dpy, CurrentTime); - } else if(boundingwin) { - endrubber(); - boundingwin=None; - boundingscr=NULL; - ungrab_server(); - XUngrabPointer(dpy, CurrentTime); - } -} - -void startbounding(Scrn *s, Window w, XEvent *e) -{ - last_icon_click=e->xbutton.time; - boundingscr=s; - boundingwin=w; - XGrabPointer(dpy, w, False, Button1MotionMask|ButtonPressMask| - ButtonReleaseMask, GrabModeAsync, GrabModeAsync, s->back, None, - CurrentTime); - grab_server(); - rubberx=e->xbutton.x; - rubbery=e->xbutton.y; - rubberx0=e->xbutton.x_root; - rubbery0=e->xbutton.y_root; - rubberw=0; - rubberh=0; - drawrubber(); - call_out(0, 0, move_dashes, NULL); -} - -void endbounding(XEvent *e) -{ - Icon *i; - int bx, by; - Window cc; - - if(boundingscr) { - remove_call_out(move_dashes, NULL); - endrubber(); - if(!(e->xbutton.state & ShiftMask)) - deselect_all_icons(boundingscr); - - if(rubberw<0) { - rubberx+=rubberw; - rubberw=-rubberw; + 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(rubberh<0) { - rubbery+=rubberh; - rubberh=-rubberh; + + if (select(max_fd, &rfds, NULL, NULL, &t) > 0) { + handle_module_input(&rfds); + if(FD_ISSET(ConnectionNumber(dpy), &rfds)) + XFlush(dpy); + continue; } - if(rubberw>=HYSTERESIS || rubberh>=HYSTERESIS) - for(i=boundingscr->icons; i; i=i->next) - if(i->window && i->mapped && - XTranslateCoordinates(dpy, i->parent, scr->back, i->x, i->y, - &bx, &by, &cc) && - bxwidth>rubberx && by+i->height>rubbery) - selecticon(i); - boundingscr=NULL; - ungrab_server(); - XUngrabPointer(dpy, CurrentTime); - } -} - -void startdragging(Client *c, XEvent *e) -{ - scr=c->scr; - dragclient=c; - XGrabPointer(dpy, c->drag, False, Button1MotionMask|ButtonPressMask| - ButtonReleaseMask, GrabModeAsync, GrabModeAsync, scr->back, - None, CurrentTime); - if(!prefs.opaquemove) - grab_server(); - initrubber(e->xbutton.x_root, e->xbutton.y_root, c); - rubberx0-=rubberx; - rubbery0-=rubbery; - if(!forcemoving) { - if(rubberx+rubberw>scr->width) - rubberx=scr->width-rubberw; - if(rubbery+rubberh>scr->height) - rubbery=scr->height-rubberh; - if(rubberx<0) - rubberx=0; - if(rubbery<0) - rubbery=0; - } - if(!prefs.opaquemove) - drawrubber(); -} - -void startscreendragging(Scrn *s, XEvent *e) -{ - dragscreen=s; - XGrabPointer(dpy, s->menubar, False, Button1MotionMask|ButtonPressMask| - ButtonReleaseMask, GrabModeAsync, GrabModeAsync, s->root, None, - CurrentTime); - olddragy=rubbery=s->y; - rubbery0=e->xbutton.y_root-rubbery; -} - -void endscreendragging() -{ - Scrn *s; - if((s=dragscreen)) { -#ifndef ASSIMILATE_WINDOWS - scrsendconfig(dragscreen); -#endif - dragscreen=NULL; - XUngrabPointer(dpy, CurrentTime); - } -} - -void abortscreendragging() -{ - if(dragscreen) { - XMoveWindow(dpy, dragscreen->back, -dragscreen->bw, - (dragscreen->y=olddragy)-dragscreen->bw); - endscreendragging(); - } -} - -void aborticondragging() -{ - if(numdragicons) { - int i; - for(i=0; iscr, - "Icon can't be moved into this window"); - aborticondragging(); -} - -void endicondragging(XEvent *e) -{ - int i; - Client *c; - int wx, wy; - Window ch; - - scr=get_front_scr(); - for(;;) { - if(scr->root == e->xbutton.root && e->xbutton.y_root>=scr->y) - break; - if((scr=scr->behind)==get_front_scr()) { - badicondrop(); - return; - } - } - - if (!scr->deftitle) { - badicondrop(); - return; - } - - if(XTranslateCoordinates(dpy, scr->root, scr->back, - e->xbutton.x_root, e->xbutton.y_root, - &wx, &wy, &ch) && ch!=None) { - if(XFindContext(dpy, ch, client_context, (XPointer*)&c) || - c->scr != scr || c->state != NormalState) - c = NULL; - } else - c = NULL; - - if(c) { - if(c->module) { - extern Atom amiwm_appwindowmsg; - XTranslateCoordinates(dpy, scr->back, c->window, -4, -4, &wx, &wy, &ch); - for(i=0; iwindow, amiwm_appwindowmsg, - dragiconlist[i].icon->window); - e->xclient.data.l[2] = dragiconlist[i].x+wx; - e->xclient.data.l[3] = dragiconlist[i].y+wy; - dispatch_event_to_broker(e, 0, c->module); + 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; } - aborticondragging(); - } else - badicondrop(); - return; + } else { + call_call_out(); + handle_module_input(&rfds); + if(FD_ISSET(ConnectionNumber(dpy), &rfds)) + XFlush(dpy); + } } - - for(i=0; imapped=0; - for(i=0; iscr!=scr) - reparenticon(dragiconlist[i].icon, scr, - dragiconlist[i].x-4, dragiconlist[i].y-4-scr->y); - else - XMoveWindow(dpy, dragiconlist[i].icon->window, - dragiconlist[i].icon->x = dragiconlist[i].x-4, - dragiconlist[i].icon->y = dragiconlist[i].y-4-scr->y); - dragiconlist[i].icon->mapped=1; - adjusticon(dragiconlist[i].icon); - } - aborticondragging(); } -void starticondragging(Scrn *scr, XEvent *e) +struct bounding { + Scrn *scr; + int x, y, w, h; + int d_offset; +}; + +static void draw_bounding(struct bounding *bounding) { - XWindowAttributes xwa; - XSetWindowAttributes xswa; - Icon *i; - Window ww; + int x = bounding->x; + int y = bounding->y; + int w = bounding->w; + int h = bounding->h; - aborticondragging(); - for(i=scr->firstselected; i; i=i->nextselected) - numdragicons++; - if(!numdragicons) + 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); + } - if(!(dragiconlist=calloc(numdragicons, sizeof(DragIcon)))) { - numdragicons=0; + 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; - for(numdragicons=0, i=scr->firstselected; i; i=i->nextselected) { - dragiconlist[numdragicons].icon = i; + dragging[nicons].icon = i; XGetWindowAttributes(dpy, i->window, &xwa); - if(i->parent!=scr->back) - XTranslateCoordinates(dpy, i->parent, scr->back, xwa.x, xwa.y, - &xwa.x, &xwa.y, &ww); - dragiconlist[numdragicons].x = xwa.x+4; - dragiconlist[numdragicons].y = xwa.y+4+scr->y; - xswa.save_under=True; - xswa.override_redirect=True; - if(i->innerwin) { + 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 = dragiconlist[numdragicons].pm = - XCreatePixmap(dpy, i->innerwin, xwa.width, xwa.height, xwa.depth); - XCopyArea(dpy, i->innerwin, dragiconlist[numdragicons].pm, scr->gc, - 0, 0, xwa.width, xwa.height, 0, 0); + 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, &xwa.x, &xwa.y, - (unsigned int *)&xwa.width, (unsigned int *)&xwa.height, - (unsigned int *)&xwa.border_width, - (unsigned int *)&xwa.depth); - } else if(i->iconpm) { - xswa.background_pixmap = i->iconpm; - XGetGeometry(dpy, i->iconpm, &xwa.root, &xwa.x, &xwa.y, - (unsigned int *)&xwa.width, (unsigned int *)&xwa.height, - (unsigned int *)&xwa.border_width, - (unsigned int *)&xwa.depth); + 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!=scr->depth) { - dragiconlist[numdragicons].pm = - XCreatePixmap(dpy, i->window, xwa.width, xwa.height, scr->depth); - XSetForeground(dpy, scr->gc, scr->dri.dri_Pens[SHADOWPEN]); - XSetBackground(dpy, scr->gc, scr->dri.dri_Pens[BACKGROUNDPEN]); - XCopyPlane(dpy, xswa.background_pixmap, dragiconlist[numdragicons].pm, - scr->gc, 0, 0, xwa.width, xwa.height, 0, 0, 1); - xswa.background_pixmap = dragiconlist[numdragicons].pm; - xwa.depth = scr->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; - dragiconlist[numdragicons].w = - XCreateWindow(dpy, scr->root, - dragiconlist[numdragicons].x, - dragiconlist[numdragicons].y, - xwa.width, xwa.height, 0, - xwa.depth, xwa.class, xwa.visual, - CWBackPixmap|CWOverrideRedirect|CWSaveUnder|CWColormap, - &xswa); + 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) { - int bShaped, xbs, ybs, cShaped, xcs, ycs; - unsigned int wbs, hbs, wcs, hcs; - XShapeQueryExtents(dpy, i->innerwin, &bShaped, &xbs, &ybs, &wbs, &hbs, - &cShaped, &xcs, &ycs, &wcs, &hcs); - if(bShaped) - XShapeCombineShape(dpy, dragiconlist[numdragicons].w, ShapeBounding, - 0, 0, i->innerwin, ShapeBounding, ShapeSet); - } else if(i->maskpm) { - XShapeCombineMask(dpy, dragiconlist[numdragicons].w, ShapeBounding, - 0, 0, i->maskpm, ShapeSet); + 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, dragiconlist[numdragicons].w); - numdragicons++; + XMapRaised(dpy, dragging[nicons].w); } - XGrabPointer(dpy, scr->back, False, Button1MotionMask|ButtonPressMask| - ButtonReleaseMask, GrabModeAsync, GrabModeAsync, scr->root, - None, CurrentTime); - olddragx = rubberx0 = e->xbutton.x_root; - olddragy = rubbery0 = e->xbutton.y_root; + + 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) { + extern Atom amiwm_appwindowmsg; + 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, 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); } -void enddragging() +static void drag_resize(Client *c, Time time, int x_root, int y_root) { - if(dragclient) { - Client *c=dragclient; - endrubber(); - if(rubbery<=-(c->scr->bh)) - rubbery=1-(c->scr->bh); - XMoveWindow(dpy, c->parent, c->x=rubberx, c->y=rubbery); - dragclient=NULL; - if(!prefs.opaquemove) - ungrab_server(); - XUngrabPointer(dpy, CurrentTime); - sendconfig(c); + 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); } -void do_icon_double_click(Scrn *scr) +static void do_icon_double_click(Scrn *scr) { extern Atom amiwm_appiconmsg; Icon *i, *next; @@ -754,36 +904,7 @@ void do_icon_double_click(Scrn *scr) } } -void startresizing(Client *c, XEvent *e) -{ - resizeclient=c; - XGrabPointer(dpy, c->resize, False, Button1MotionMask|ButtonPressMask| - ButtonReleaseMask, GrabModeAsync, GrabModeAsync, - c->scr->back, None, CurrentTime); - if(!prefs.opaqueresize) - grab_server(); - initrubber(e->xbutton.x_root, e->xbutton.y_root, c); - rubberx0-=rubberw; - rubbery0-=rubberh; - if(!prefs.opaqueresize) - drawrubber(); -} - -void endresizing() -{ - extern void resizeclientwindow(Client *c, int, int); - if(resizeclient) { - Client *c=resizeclient; - endrubber(); - if(!prefs.opaqueresize) - ungrab_server(); - resizeclientwindow(c, rubberw, rubberh); - resizeclient=NULL; - XUngrabPointer(dpy, CurrentTime); - } -} - -void abortfocus() +static void abortfocus() { if(activeclient) { activeclient->active=False; @@ -797,7 +918,7 @@ void abortfocus() setfocus(None); } -RETSIGTYPE sighandler(int sig) +static RETSIGTYPE sighandler(int sig) { signalled=1; signal(sig, SIG_IGN); @@ -856,7 +977,7 @@ static void update_clock(void *dontcare) } while(scr != get_front_scr()); } -void cleanup() +static void cleanup() { int sc; extern void free_prefs(); @@ -1007,8 +1128,7 @@ int main(int argc, char *argv[]) while((!signalled) && QLength(dpy)>0) { Client *c; Icon *i; - int motionx, motiony; - + XNextEvent(dpy, &event); if(!XFindContext(dpy, event.xany.window, client_context, (XPointer*)&c)) { @@ -1026,16 +1146,12 @@ int main(int argc, char *argv[]) switch(event.type) { case Expose: if(!event.xexpose.count) { - if((rubberclient || boundingscr)&&!prefs.opaquemove - &&!prefs.opaqueresize) - drawrubber(); if(c) redraw(c, event.xexpose.window); else if(i) redrawicon(i, event.xexpose.window); else if(scr) redrawmenubar(scr, event.xexpose.window); - if((rubberclient || boundingscr)&&!prefs.opaquemove) drawrubber(); } break; case CreateNotify: @@ -1316,9 +1432,7 @@ int main(int argc, char *argv[]) } break; case ButtonPress: - if(!rubberclient && !boundingscr && clickwindow==None && - !dragiconlist && event.xbutton.button==Button1 && - !dragscreen && !menuactive) + if(clickwindow==None && event.xbutton.button==Button1 && !menuactive) { if(c) { if((!c->active) && prefs.focus==FOC_CLICKTOTYPE && (c->state==NormalState)) { @@ -1350,9 +1464,9 @@ int main(int argc, char *argv[]) if(event.xbutton.window==c->drag) { forcemoving=(prefs.forcemove==FM_ALWAYS) || (event.xbutton.state & ShiftMask); - startdragging(c, &event); + drag_move(c, event.xbutton.time, event.xbutton.x_root, event.xbutton.y_root); } else if(event.xbutton.window==c->resize) - startresizing(c, &event); + 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) ; @@ -1367,7 +1481,7 @@ int main(int argc, char *argv[]) deselect_all_icons(i->scr); last_icon_click=event.xbutton.time; selecticon(i); - starticondragging(i->scr, &event); + 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; @@ -1375,26 +1489,21 @@ int main(int argc, char *argv[]) redrawmenubar(scr, scr->menubardepth); } else if(scr&&event.xbutton.window==scr->menubar && scr->back!=scr->root) { - startscreendragging(scr, &event); - } - else if(scr&&scr->back==event.xbutton.window) { + drag_screen(scr, event.xbutton.time, event.xbutton.x_root, event.xbutton.y_root); + } else if(scr&&scr->back==event.xbutton.window) { abortfocus(); - startbounding(scr, scr->back, &event); + 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(rubberclient || boundingscr) - abortrubber(); - else if(scr&&(scr==mbdscr)&&clickwindow==scr->menubardepth) { + } else if(event.xbutton.button==3) { + if(scr&&(scr==mbdscr)&&clickwindow==scr->menubardepth) { mbdclick=NULL; clickwindow=None; redrawmenubar(scr, scr->menubardepth); - } else if(clickclient) + } else if(clickclient) { gadgetaborted(clickclient); - else if(dragiconlist) - aborticondragging(); - else if(dragscreen) - abortscreendragging(); - else if(scr&&!menuactive) { + } else if(scr&&!menuactive) { menu_on(); menuactive=scr; } @@ -1407,17 +1516,8 @@ int main(int argc, char *argv[]) break; case ButtonRelease: if(event.xbutton.button==Button1) { - if(rubberclient) { - if(dragclient) enddragging(); - else if(resizeclient) endresizing(); - } else if(boundingwin) - endbounding(&event); - else if(clickclient) + if(clickclient) gadgetunclicked(clickclient, &event); - else if(dragiconlist) { - endicondragging(&event); - } else if(dragscreen) - endscreendragging(); else if((scr=mbdscr)&& clickwindow==scr->menubardepth) { if(mbdclick) { mbdclick=NULL; @@ -1432,102 +1532,6 @@ int main(int argc, char *argv[]) } break; case MotionNotify: - do { - motionx=event.xmotion.x_root; - motiony=event.xmotion.y_root; - } while(XCheckTypedEvent(dpy, MotionNotify, &event)); - if(dragclient) { - scr=dragclient->scr; - if(!prefs.opaquemove) - drawrubber(); - rubberx=motionx-rubberx0; - rubbery=motiony-rubbery0; - if(!forcemoving) { - if(prefs.forcemove==FM_AUTO && - (rubberx+rubberw-scr->width>(rubberw>>2) || - rubbery+rubberh-scr->height>(rubberh>>2) || - -rubberx>(rubberw>>2)|| - -rubbery>(rubberh>>2))) - forcemoving=1; - else { - if(rubberx+rubberw>scr->width) - rubberx=scr->width-rubberw; - if(rubbery+rubberh>scr->height) - rubbery=scr->height-rubberh; - if(rubberx<0) - rubberx=0; - if(rubbery<0) - rubbery=0; - } - } - if(prefs.opaquemove) { - if(rubbery<=-(c->scr->bh)) - rubbery=1-(c->scr->bh); - XMoveWindow(dpy, c->parent, c->x=rubberx, c->y=rubbery); - } else { - drawrubber(); - } - } else if(resizeclient) { - int rw=rubberw, rh=rubberh; - scr=resizeclient->scr; - if(resizeclient->sizehints->width_inc) { - rw=motionx-rubberx0-resizeclient->sizehints->base_width- - resizeclient->framewidth; - rw-=rw%resizeclient->sizehints->width_inc; - rw+=resizeclient->sizehints->base_width; - if(rw>resizeclient->sizehints->max_width) - rw=resizeclient->sizehints->max_width; - if(rwsizehints->min_width) - rw=resizeclient->sizehints->min_width; - rw+=resizeclient->framewidth; - } - if(resizeclient->sizehints->height_inc) { - rh=motiony-rubbery0-resizeclient->sizehints->base_height- - resizeclient->frameheight; - rh-=rh%resizeclient->sizehints->height_inc; - rh+=resizeclient->sizehints->base_height; - if(rh>resizeclient->sizehints->max_height) - rh=resizeclient->sizehints->max_height; - if(rhsizehints->min_height) - rh=resizeclient->sizehints->min_height; - rh+=resizeclient->frameheight; - } - if(rw!=rubberw || rh!=rubberh) { - if(prefs.opaqueresize) { - Client *c = resizeclient; - extern void resizeclientwindow(Client *c, int, int); - rubberw=rw; - rubberh=rh; - resizeclientwindow(c, rubberw, rubberh); - } else { - drawrubber(); - rubberw=rw; - rubberh=rh; - drawrubber(); - } - } - } else if(dragiconlist) { - int i; - for(i=0; i=dragscreen->height) - rubbery=dragscreen->height-1; - XMoveWindow(dpy, dragscreen->back, -dragscreen->bw, - (dragscreen->y=rubbery)-dragscreen->bw); - } else if(boundingscr) { - drawrubber(); - rubberw=motionx-rubberx0; - rubberh=motiony-rubbery0; - drawrubber(); - } break; case KeyPress: if(!dispatch_event_to_broker(&event, KeyPressMask, modules))