Files
amiwm-neo/client.c
Lucas de Sena beccf8b0fb do not reparent client window; fix amiwm#29
reparenting the client's window creates a few issues:
• We get bogus UnmapNotify and MapNotify events that are ultimately
  generated by amiwm itself.
• We do not get button events from the window, since its frame
  (c->parent) that we have called XGrabButtons(3) onto, is not
  its parent anymore.

To fix this, do not reparent the client window, but its frame window.
And keep the client window always inside the frame.
2026-02-19 20:46:57 +00:00

474 lines
12 KiB
C

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "drawinfo.h"
#include "screen.h"
#include "icon.h"
#include "client.h"
#include "icc.h"
#include "prefs.h"
#ifdef AMIGAOS
#include <pragmas/xlib_pragmas.h>
extern struct Library *XLibBase;
#endif
extern Display *dpy;
extern XContext client_context, screen_context;
extern Client *activeclient;
extern Scrn *menuactive;
extern void setfocus(Window);
Client *clients=NULL;
void grav_map_frame_to_win(Client *c, int x0, int y0, int *x, int *y)
{
switch(c->gravity) {
case EastGravity:
case NorthEastGravity:
case SouthEastGravity:
*x=x0+c->framewidth-c->old_bw-c->old_bw; break;
case CenterGravity:
case NorthGravity:
case SouthGravity:
*x=x0+4-c->old_bw; break;
case WestGravity:
case NorthWestGravity:
case SouthWestGravity:
default:
*x=x0; break;
}
switch(c->gravity) {
case SouthGravity:
case SouthEastGravity:
case SouthWestGravity:
*y=y0+c->frameheight-c->old_bw-c->old_bw; break;
case CenterGravity:
case EastGravity:
case WestGravity:
*y=y0+c->scr->bh-c->old_bw; break;
case NorthGravity:
case NorthEastGravity:
case NorthWestGravity:
default:
*y=y0; break;
}
}
void grav_map_win_to_frame(Client *c, int x0, int y0, int *x, int *y)
{
switch(c->gravity) {
case EastGravity:
case NorthEastGravity:
case SouthEastGravity:
*x=x0-c->framewidth+c->old_bw+c->old_bw; break;
case CenterGravity:
case NorthGravity:
case SouthGravity:
*x=x0-4+c->old_bw; break;
case WestGravity:
case NorthWestGravity:
case SouthWestGravity:
default:
*x=x0; break;
}
switch(c->gravity) {
case SouthGravity:
case SouthEastGravity:
case SouthWestGravity:
*y=y0-c->frameheight+c->old_bw+c->old_bw; break;
case CenterGravity:
case EastGravity:
case WestGravity:
*y=y0-c->scr->bh+c->old_bw; break;
case NorthGravity:
case NorthEastGravity:
case NorthWestGravity:
default:
*y=y0; break;
}
}
void sendconfig(Client *c)
{
XConfigureEvent ce;
ce.type = ConfigureNotify;
ce.event = c->window;
ce.window = c->window;
/*
grav_map_frame_to_win(c, c->x, c->y, &ce.x, &ce.y);
*/
ce.x = c->x+4;
#ifndef ASSIMILATE_WINDOWS
ce.y = c->y+c->scr->bh+c->scr->y;
#else
ce.y = c->y+c->scr->bh;
#endif
ce.width = c->pwidth-c->framewidth;
ce.height = c->pheight-c->frameheight;
ce.border_width = c->old_bw;
ce.above = None;
ce.override_redirect = 0;
XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent*)&ce);
XSync(dpy, False);
}
void scrsendconfig(Scrn *s)
{
Client *c;
for (c=clients; c; c = c->next)
if(c->scr == s)
sendconfig(c);
}
void checksizehints(Client *c)
{
long supplied;
XGetWMNormalHints(dpy, c->window, c->sizehints, &supplied);
if(!(c->sizehints->flags&PMinSize))
c->sizehints->min_width=c->sizehints->min_height=0;
if(!(c->sizehints->flags&PMaxSize))
c->sizehints->max_width=c->sizehints->max_height=1<<30;
if(!(c->sizehints->flags&PResizeInc))
c->sizehints->width_inc=c->sizehints->height_inc=1;
if(c->sizehints->flags&PBaseSize) {
c->sizehints->min_width=c->sizehints->base_width;
c->sizehints->min_height=c->sizehints->base_height;
}
if(c->sizehints->min_width<1) c->sizehints->min_width=1;
if(c->sizehints->min_height<1) c->sizehints->min_height=1;
c->sizehints->base_width=c->sizehints->min_width;
c->sizehints->base_height=c->sizehints->min_height;
if(c->sizehints->flags&PWinGravity) c->gravity=c->sizehints->win_gravity;
}
void open_fscrn(Client *c)
{
c->fsscr = scr = openscreen(NULL, scr->root);
c->reparenting = 1;
XReparentWindow(dpy, c->parent, c->fsscr->back, 0, 0);
XRaiseWindow(dpy, c->window);
XMoveResizeWindow(dpy, c->parent, 0, 0, c->fsscr->width, c->fsscr->height);
XMoveResizeWindow(dpy, c->window, 0, 0, c->fsscr->width, c->fsscr->height);
for (Client *dialog = clients; dialog != NULL; dialog = dialog->next)
if (dialog->leader == c)
reparent_client(c->fsscr, dialog);
XResizeWindow(dpy, c->window, scr->width, scr->height);
realizescreens();
scr = c->fsscr;
#ifdef USE_FONTSETS
XStoreName(dpy, scr->back, c->title);
#else
XSetWMName(dpy, scr->back, &c->title);
#endif
screentoback();
}
void close_fscrn(Client *c, int state)
{
if (c->fsscr == NULL)
return;
XReparentWindow(dpy, c->parent, c->scr, c->x, c->y);
XMoveResizeWindow(dpy, c->parent, c->x, c->y, c->pwidth, c->pheight);
XMoveResizeWindow(dpy, c->window, 4, c->scr->bh, c->pwidth-c->framewidth, c->pheight-c->frameheight);
XResizeWindow(dpy, c->window, c->pwidth-c->framewidth, c->pheight-c->frameheight);
XLowerWindow(dpy, c->window);
scr = c->fsscr;
closescreen();
c->fsscr = NULL;
scr = c->scr;
if (state != IconicState)
XMapWindow(dpy, c->parent);
for (Client *dialog = clients; dialog != NULL; dialog = dialog->next)
if (dialog->leader == c)
reparent_client(c->scr, dialog);
}
void setclientstate(Client *c, int state)
{
long data[2];
data[0] = (long) state;
data[1] = (long) None;
if (c->fullscreen) {
if (state != NormalState && c->state == NormalState) {
close_fscrn(c, state);
} else if (state == NormalState && c->state != NormalState) {
open_fscrn(c);
}
}
c->state = state;
XChangeProperty(dpy, c->window, wm_state, wm_state, 32,
PropModeReplace, (unsigned char *)data, 2);
}
void getstate(Client *c)
{
long *data=NULL;
if(_getprop(c->window, wm_state, wm_state, 2l, (char **)&data)>0) {
c->state=*data;
XFree((char *)data);
}
}
Client *createclient(Window w)
{
extern void checkstyle(Client *c);
XWindowAttributes attr;
Client *c;
int b = 0;
if(w==0) return 0;
if(!XFindContext(dpy, w, client_context, (XPointer*)&c)) return c;
XGetWindowAttributes(dpy, w, &attr);
c = (Client *)calloc(1, sizeof(Client));
c->sizehints = XAllocSizeHints();
c->scr = scr;
c->fsscr = NULL;
c->window = w;
c->parent = scr->root;
c->old_bw = attr.border_width;
c->next = clients;
c->state = WithdrawnState;
c->gravity = NorthWestGravity;
c->reparenting = 0;
c->fullscreen = 0;
XSelectInput(dpy, c->window, PropertyChangeMask);
#ifdef USE_FONTSETS
{
XTextProperty prop;
c->title = NULL;
if(XGetWMName(dpy, c->window, &prop) && prop.value) {
char **list;
int n;
if(XmbTextPropertyToTextList(dpy, &prop, &list, &n) >= Success) {
if(n > 0)
c->title = strdup(list[0]);
XFreeStringList(list);
}
XFree(prop.value);
}
}
#else
XGetWMName(dpy, c->window, &c->title);
#endif
c->style = NULL;
checkstyle(c);
checksizehints(c);
c->zoomx=0;
c->zoomy=scr->bh;
if ((c->sizehints->width_inc &&
c->sizehints->min_width+c->sizehints->width_inc<=c->sizehints->max_width)||
(c->sizehints->height_inc &&
c->sizehints->min_height+c->sizehints->height_inc<=c->sizehints->max_height))
b = prefs.sizeborder;
if(c->sizehints->width_inc) {
c->zoomw=scr->width-c->sizehints->base_width;
if (b & Psizeright)
c->zoomw-=22;
else
c->zoomw-=8;
c->zoomw-=c->zoomw%c->sizehints->width_inc;
c->zoomw+=c->sizehints->base_width;
if(c->zoomw>c->sizehints->max_width)
c->zoomw=c->sizehints->max_width;
if(c->zoomw<c->sizehints->min_width)
c->zoomw=c->sizehints->min_width;
} else
c->zoomw=attr.width;
if(c->sizehints->height_inc) {
c->zoomh=scr->height-c->sizehints->base_height-scr->bh-c->zoomy;
if (b & Psizebottom)
c->zoomh -= 10;
else
c->zoomh -= 2;
c->zoomh-=c->zoomh%c->sizehints->height_inc;
c->zoomh+=c->sizehints->base_height;
if(c->zoomh>c->sizehints->max_height)
c->zoomh=c->sizehints->max_height;
if(c->zoomh<c->sizehints->min_height)
c->zoomh=c->sizehints->min_height;
} else
c->zoomh=attr.height;
XSaveContext(dpy, w, client_context, (XPointer)c);
return clients = c;
}
void rmclient(Client *c)
{
Client *cc;
if (c == clients)
clients = c->next;
else
if((cc = clients))
for (; cc->next; cc = cc->next)
if (cc->next == c) {
cc->next = cc->next->next;
break;
}
if ((scr = c->fsscr))
closescreen();
scr = c->scr;
if(c->active) {
if(!menuactive)
setfocus(None);
c->active=False;
activeclient = NULL;
XInstallColormap(dpy, scr->cmap);
} else if(prefs.focus==FOC_CLICKTOTYPE)
XUngrabButton(dpy, Button1, AnyModifier, c->parent);
#ifdef USE_FONTSETS
if(c->title)
free(c->title);
#else
if(c->title.value)
XFree(c->title.value);
#endif
if(c->parent != c->scr->root) {
XDestroyWindow(dpy, c->parent);
XDeleteContext(dpy, c->parent, client_context);
}
if(c->close)
XDeleteContext(dpy, c->close, client_context);
if(c->drag)
XDeleteContext(dpy, c->drag, client_context);
if(c->iconify)
XDeleteContext(dpy, c->iconify, client_context);
if(c->zoom)
XDeleteContext(dpy, c->zoom, client_context);
if(c->depth)
XDeleteContext(dpy, c->depth, client_context);
if(c->resize)
XDeleteContext(dpy, c->resize, client_context);
if(c->icon)
rmicon(c->icon);
if(c->window)
XDeleteContext(dpy, c->window, client_context);
if (c->sizehints)
XFree(c->sizehints);
for (Client *dialog = clients; dialog != NULL; dialog = dialog->next)
if (dialog->leader == c)
dialog->leader = NULL;
free(c);
}
int screen_has_clients()
{
int n = 0;
Client *c = clients;
while(c) {
if(c->scr == scr)
n++;
c = c->next;
}
return n;
}
void flushclients()
{
unsigned int i, nwins;
Window dw1, dw2, *wins;
Client *c;
#ifdef ASSIMILATE_WINDOWS
Scrn *scr2;
#endif
if((scr = get_front_scr())) do {
scr = scr->upfront;
XQueryTree(dpy, scr->back, &dw1, &dw2, &wins, &nwins);
for(i=0; i<nwins; i++)
if((!XFindContext(dpy, wins[i], client_context, (XPointer *)&c))&&wins[i]==c->parent) {
int x,y;
grav_map_frame_to_win(c, c->x, c->y, &x, &y);
XReparentWindow(dpy, c->window, scr->root, x, y);
XSetWindowBorderWidth(dpy, c->window, c->old_bw);
XRemoveFromSaveSet(dpy, c->window);
wins[i]=c->window;
rmclient(c);
}
#ifdef ASSIMILATE_WINDOWS
else if((!XFindContext(dpy, wins[i], screen_context, (XPointer *)&scr2)) && scr2==scr) {
XWindowAttributes attr;
XSetWindowAttributes xsa;
XGetWindowAttributes(dpy, wins[i], &attr);
XReparentWindow(dpy, wins[i], scr->root, attr.x, attr.y);
xsa.override_redirect = True;
XChangeWindowAttributes(dpy, wins[i], CWOverrideRedirect, &xsa);
XDeleteContext(dpy, wins[i], screen_context);
XRemoveFromSaveSet(dpy, wins[i]);
}
#endif
/*
if(nwins) {
for(i=0; i<(nwins>>1); i++) {
Window w=wins[i];
wins[i]=wins[nwins-1-i];
wins[nwins-1-i]=w;
}
XRestackWindows(dpy, wins, nwins);
}
*/
XFree((void *) wins);
} while( scr!= get_front_scr());
while((c=clients)) {
if(c->parent != c->scr->root) {
int x,y;
grav_map_frame_to_win(c, c->x, c->y, &x, &y);
XReparentWindow(dpy, c->window, c->scr->root, x, y);
XSetWindowBorderWidth(dpy, c->window, c->old_bw);
XRemoveFromSaveSet(dpy, c->window);
}
rmclient(c);
}
}
/*
* Reparent the given client to the given screen.
*
* This moves the given client to the given screen.
*/
void
reparent_client(Scrn *s, Client *client)
{
client->scr = s;
if(client->parent != client->scr->root)
XReparentWindow(dpy, client->parent, s->back, client->x, client->y);
if (s->deftitle != NULL)
setstringprop(client->window, amiwm_screen, s->deftitle);
sendconfig(client);
}
void fullscreen(Client *c, int fs)
{
if (fs == c->fullscreen)
return;
if (c->state != NormalState) {
c->fullscreen = fs;
setwmstate(c);
return;
}
if (fs) {
open_fscrn(c);
c->fullscreen = 1;
} else {
close_fscrn(c, c->state);
c->fullscreen = 0;
setclientstate(c, NormalState);
}
setwmstate(c);
}