/***********************************************************/
/* */
/* System dependent window management (unix, x11) */
/* */
/***********************************************************/
#include "unix/guts.h"
#include "Menu.h"
#include "Icon.h"
#include "Window.h"
#include "Application.h"
/* Tell a NET-compliant window manager that the window needs special treatment.
See freedesktop.org for docs
params - 0 - clear, 1 - set
*/
static void
set_net_hint(XWindow window, Bool state, Atom prop1, Atom prop2)
{
XClientMessageEvent ev;
if ( guts. icccm_only) return;
/* Send change message to root window, it's responsible for
on-the-fly changes. Otherwise, the properties are not re-read
until next XMapWindow() */
bzero( &ev, sizeof(ev));
ev. type = ClientMessage;
ev. display = DISP;
ev. window = window;
ev. message_type = NET_WM_STATE;
ev. format = 32;
/*
_NET_WM_STATE_REMOVE 0 // remove/unset property
_NET_WM_STATE_ADD 1 // add/set property
_NET_WM_STATE_TOGGLE 2 // toggle property
*/
ev. data. l[0] = state ? 1 : 0;
ev. data. l[1] = (long)prop1;
ev. data. l[2] = (long)prop2;
XSendEvent( DISP, guts. root, false, SubstructureRedirectMask|SubstructureNotifyMask, (XEvent*)&ev);
}
#define NETWM_SET_TASK_LISTED(xwindow,flag) set_net_hint(xwindow,flag,NET_WM_STATE_SKIP_TASKBAR,0)
#define NETWM_SET_MODAL(xwindow,flag) set_net_hint(xwindow,flag,NET_WM_STATE_MODAL,0)
#define NETWM_SET_MAXIMIZED(xwindow,flag) set_net_hint(xwindow,flag,NET_WM_STATE_MAXIMIZED_VERT,NET_WM_STATE_MAXIMIZED_HORZ)
#define NETWM_SET_ON_TOP(xwindow,flag) set_net_hint(xwindow,flag,NET_WM_STATE_STAYS_ON_TOP,NET_WM_STATE_ABOVE)
unsigned char *
prima_get_window_property( XWindow window, Atom property, Atom req_type, Atom * actual_type,
int * actual_format, unsigned long * nitems)
{
Atom a_actual_type;
unsigned char * ret, * ptr;
unsigned long left, n, a_nitems;
int a_actual_format, curr_size, new_size, malloc_size, offset;
ret = NULL;
offset = 0;
new_size = curr_size = malloc_size = 0;
if ( actual_type == NULL) actual_type = &a_actual_type;
if ( actual_format == NULL) actual_format = &a_actual_format;
if ( nitems == NULL) nitems = &a_nitems;
*nitems = 0;
while ( XGetWindowProperty( DISP, window, property, offset, 2048, false, req_type,
actual_type, actual_format, &n, &left, &ptr) == Success) {
if ( ptr) {
if ( n > 0) {
if ( *actual_format == 32) *actual_format = sizeof(long) * 8; /* MUAHAHA!! That's even documented now */
curr_size = n * *actual_format / 8;
new_size += curr_size;
offset += curr_size / 4;
*nitems += n;
if ( ret == NULL) {
malloc_size = new_size;
ret = malloc( malloc_size);
if ( ret == NULL) {
warn("Not enough memory: %d bytes\n", malloc_size);
return NULL;
}
} else {
if ( new_size > malloc_size) {
unsigned char * p;
malloc_size = new_size * 2;
p = realloc( ret, malloc_size);
if ( p) {
ret = p;
} else {
free( ret);
warn("Not enough memory: %d bytes\n", malloc_size);
return NULL;
}
}
}
memcpy( ret + new_size - curr_size, ptr, curr_size);
}
XFree( ptr);
}
if ( left <= 0) break;
}
return ret;
}
Bool
prima_wm_net_state_read_maximization( XWindow window, Atom property)
/*
reads property, returns true if it has both vertical and horizontal properties set.
*/
{
long * prop;
unsigned long i, n;
int horiz = 0, vert = 0;
if ( guts. icccm_only) return false;
prop = ( long *) prima_get_window_property( window, property, XA_ATOM, NULL, NULL, &n);
if ( !prop) return false;
for ( i = 0; i < n; i++) {
if ( prop[i] == NET_WM_STATE_MAXIMIZED_VERT) vert = 1;
/* KDE v2 defines _HORIZ, KDE v3 defines _HORZ - a horrible hack follows */
else if ( prop[i] == guts. atoms[ AI_NET_WM_STATE_MAXIMIZED_HORZ]) {
if ( guts. net_wm_maximize_HORZ_vs_HORIZ == 0) {
guts. net_wm_maximize_HORZ_vs_HORIZ = AI_NET_WM_STATE_MAXIMIZED_HORZ;
Mdebug("wm: kde-3 style detected\n");
}
horiz = 1;
}
else if ( prop[i] == guts. atoms[ AI_NET_WM_STATE_MAXIMIZED_HORIZ]) {
if ( guts. net_wm_maximize_HORZ_vs_HORIZ == 0) {
guts. net_wm_maximize_HORZ_vs_HORIZ = AI_NET_WM_STATE_MAXIMIZED_HORIZ;
Mdebug("wm: kde-2 style detected\n");
}
horiz = 1;
}
}
free( prop);
return vert && horiz;
}
static Bool
net_supports_maximization(void)
/* If WM supports customization, root.NET_SUPPORTED contains NET_WM_STATE_MAXIMIZED atoms.
Stores result in guts. net_wm_maximization, so ConfigureEvent handler doesn't apply
maximization heuristics. */
{
Bool has_max;
has_max = prima_wm_net_state_read_maximization( guts. root, NET_SUPPORTED);
if ( has_max != guts. net_wm_maximization) {
guts. net_wm_maximization = has_max;
Mdebug( has_max ? "wm: supports maximization\n" : "win: WM quits supporting maximization\n");
}
return has_max;
}
static void
apc_window_task_listed( Handle self, Bool task_list)
{
DEFXX;
XX-> flags. task_listed = ( task_list ? 1 : 0);
NETWM_SET_TASK_LISTED( X_WINDOW, XX-> flags.task_listed );
}
/* Motif window hints */
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
/* bit definitions for MwmHints.functions */
#define MWM_FUNC_ALL (1L << 0)
#define MWM_FUNC_RESIZE (1L << 1)
#define MWM_FUNC_MOVE (1L << 2)
#define MWM_FUNC_MINIMIZE (1L << 3)
#define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNC_CLOSE (1L << 5)
/* bit definitions for MwmHints.decorations */
#define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1L << 6)
static void
set_motif_hints( XWindow window, int border_style, int border_icons)
{
struct {
unsigned long flags, functions, decorations;
long input_mode;
unsigned long status;
} mwmhints;
#define MWMHINT_OR(field,value) mwmhints.field |= (value)
if ( guts. icccm_only) return;
bzero( &mwmhints, sizeof(mwmhints));
MWMHINT_OR( flags, MWM_HINTS_DECORATIONS);
MWMHINT_OR( flags, MWM_HINTS_FUNCTIONS);
if ( border_style == bsSizeable) {
MWMHINT_OR( decorations, MWM_DECOR_BORDER);
MWMHINT_OR( decorations, MWM_DECOR_RESIZEH);
MWMHINT_OR( functions, MWM_FUNC_RESIZE);
}
MWMHINT_OR( functions, MWM_FUNC_MOVE);
MWMHINT_OR( functions, MWM_FUNC_CLOSE);
if ( border_icons & biTitleBar)
MWMHINT_OR( decorations, MWM_DECOR_TITLE);
if ( border_icons & biSystemMenu)
MWMHINT_OR( decorations, MWM_DECOR_MENU);
if ( border_icons & biMinimize) {
MWMHINT_OR( decorations, MWM_DECOR_MINIMIZE);
MWMHINT_OR( functions, MWM_FUNC_MINIMIZE);
}
if (( border_icons & biMaximize) && ( border_style == bsSizeable)) {
MWMHINT_OR( decorations, MWM_DECOR_MAXIMIZE);
MWMHINT_OR( functions, MWM_FUNC_MAXIMIZE);
}
XChangeProperty(DISP, window, XA_MOTIF_WM_HINTS, XA_MOTIF_WM_HINTS, 32,
PropModeReplace, (unsigned char *) &mwmhints, 5);
}
Bool
apc_window_create( Handle self, Handle owner, Bool sync_paint, int border_icons,
int border_style, Bool task_list, int window_state,
int on_top, Bool use_origin, Bool use_size)
{
DEFXX;
Handle real_owner;
XSizeHints hints;
XSetWindowAttributes attrs;
Point p0 = {0,0};
Atom atoms[ 2];
XWMHints wmhints;
XClassHint *class_hint;
ConfigureEventPair *cep;
if ( border_style != bsSizeable) border_style = bsDialog;
border_icons &= biAll;
if ( X_WINDOW) { /* recreate request */
Bool destructive_motif_hints = 0; /* KDE 3.1: setting motif hints kills net_wm hints */
if ( !guts.icccm_only && (
( border_style != ( XX-> flags. sizeable ? bsSizeable : bsDialog)) ||
( border_icons != XX-> borderIcons) ||
( on_top >= 0)
)) {
Bool visible = XX-> flags. mapped;
if (( border_style != ( XX-> flags. sizeable ? bsSizeable : bsDialog)) ||
( border_icons != XX-> borderIcons))
destructive_motif_hints = 1;
if ( destructive_motif_hints && on_top < 0)
on_top = apc_window_get_on_top( self);
if ( destructive_motif_hints)
set_motif_hints( X_WINDOW, border_style, border_icons);
if ( on_top >= 0)
NETWM_SET_ON_TOP( X_WINDOW, on_top);
XX-> borderIcons = border_icons;
XX-> flags. sizeable = ( border_style == bsSizeable) ? 1 : 0;
}
if (
(( task_list ? 1 : 0) != ( XX-> flags. task_listed ? 1 : 0))
|| destructive_motif_hints
)
apc_window_task_listed( self, task_list);
return true;
}
/* create */
attrs. event_mask = 0
| KeyPressMask /* Key events unmasked for both windows, since */
| KeyReleaseMask /* focusing is unpredicatble for some WM */
/*| ButtonPressMask */
/*| ButtonReleaseMask */
/*| EnterWindowMask */
/*| LeaveWindowMask */
/*| PointerMotionMask */
/* | PointerMotionHintMask */
/* | Button1MotionMask */
/* | Button2MotionMask */
/* | Button3MotionMask */
/* | Button4MotionMask */
/* | Button5MotionMask */
/*| ButtonMotionMask */
/*| KeymapStateMask */
| ExposureMask
| VisibilityChangeMask
| StructureNotifyMask
/* | ResizeRedirectMask */
/* | SubstructureNotifyMask */
/* | SubstructureRedirectMask */
| FocusChangeMask
| PropertyChangeMask
| ColormapChangeMask
| OwnerGrabButtonMask;
attrs. override_redirect = false;
attrs. do_not_propagate_mask = attrs. event_mask;
attrs. colormap = guts. defaultColormap;
X_WINDOW = XCreateWindow( DISP, guts. root,
0, 0, 1, 1, 0, guts. visual. depth,
InputOutput, VISUAL,
0
/* | CWBackPixmap */
/* | CWBackPixel */
/* | CWBorderPixmap */
/* | CWBorderPixel */
/* | CWBitGravity */
/* | CWWinGravity */
/* | CWBackingStore */
/* | CWBackingPlanes */
/* | CWBackingPixel */
| CWOverrideRedirect
/* | CWSaveUnder */
| CWEventMask
/* | CWDontPropagate */
| CWColormap
/* | CWCursor */
, &attrs);
if (!X_WINDOW) return false;
attrs. event_mask = 0
| KeyPressMask
| KeyReleaseMask
| ButtonPressMask
| ButtonReleaseMask
| EnterWindowMask
| LeaveWindowMask
| PointerMotionMask
/* | PointerMotionHintMask */
/* | Button1MotionMask */
/* | Button2MotionMask */
/* | Button3MotionMask */
/* | Button4MotionMask */
/* | Button5MotionMask */
| ButtonMotionMask
| KeymapStateMask
| ExposureMask
| VisibilityChangeMask
| StructureNotifyMask
/* | ResizeRedirectMask */
/* | SubstructureNotifyMask */
/* | SubstructureRedirectMask */
| FocusChangeMask
| PropertyChangeMask
| ColormapChangeMask
| OwnerGrabButtonMask;
attrs. override_redirect = false;
attrs. do_not_propagate_mask = attrs. event_mask;
attrs. colormap = guts. defaultColormap;
XX-> client = XCreateWindow( DISP, X_WINDOW,
0, 0, 1, 1, 0, guts. visual. depth,
InputOutput, VISUAL,
0
/* | CWBackPixmap */
/* | CWBackPixel */
/* | CWBorderPixmap */
/* | CWBorderPixel */
/* | CWBitGravity */
/* | CWWinGravity */
/* | CWBackingStore */
/* | CWBackingPlanes */
/* | CWBackingPixel */
| CWOverrideRedirect
/* | CWSaveUnder */
| CWEventMask
/* | CWDontPropagate */
| CWColormap
/* | CWCursor */
, &attrs);
if (!XX-> client) return false;
hash_store( guts.windows, &XX-> client, sizeof(XX-> client), (void*)self);
hash_store( guts.windows, &X_WINDOW, sizeof(X_WINDOW), (void*)self);
XCHECKPOINT;
XMapWindow( DISP, XX-> client);
XX-> flags. iconic = ( window_state == wsMinimized) ? 1 : 0;
wmhints. flags = InputHint | StateHint;
wmhints. input = false;
wmhints. initial_state = XX-> flags. iconic ? IconicState : NormalState;
XSetWMHints( DISP, X_WINDOW, &wmhints);
XCHECKPOINT;
atoms[ 0] = WM_DELETE_WINDOW;
atoms[ 1] = WM_TAKE_FOCUS;
XSetWMProtocols( DISP, X_WINDOW, atoms, 2);
XCHECKPOINT;
if (( class_hint = XAllocClassHint()) != nil) {
class_hint-> res_class = PComponent(application)-> name;
class_hint-> res_name = CObject( self)-> className;
XSetClassHint( DISP, X_WINDOW, class_hint);
XFree (class_hint);
}
if ( guts. hostname. value)
XSetWMClientMachine(DISP, X_WINDOW, &guts. hostname);
XSetCommand(DISP, X_WINDOW, PL_origargv, PL_origargc);
set_motif_hints( X_WINDOW, border_style, border_icons);
XX-> borderIcons = border_icons;
XX-> type.drawable = true;
XX-> type.widget = true;
XX-> type.window = true;
real_owner = application;
XX-> parent = guts. root;
XX-> real_parent = nilHandle;
XX-> udrawable = XX-> gdrawable = XX-> client;
XX-> flags. clip_owner = false;
XX-> flags. sync_paint = sync_paint;
XX-> flags. task_listed = 1;
XX-> above = nilHandle;
XX-> owner = real_owner;
apc_component_fullname_changed_notify( self);
prima_send_create_event( X_WINDOW);
if ( on_top > 0) NETWM_SET_ON_TOP( X_WINDOW, 1);
apc_window_task_listed( self, task_list);
if ( border_style == bsSizeable) XX-> flags. sizeable = 1;
/* setting initial size */
{
int nrects;
Rect2 * monitors;
monitors = apc_application_get_monitor_rects( nilHandle, &nrects);
if ( nrects > 0 ) {
int i, min_x = monitors[0].x, min_y = monitors[0].y;
XX-> size.x = monitors[0].width;
XX-> size.y = monitors[0].height;
for ( i = 1; i < nrects; i++) {
if ( min_x > monitors[i].x && min_y > monitors[i].y ) {
min_x = monitors[i].x;
min_y = monitors[i].y;
XX-> size.x = monitors[i].width;
XX-> size.y = monitors[i].height;
}
}
} else {
XX-> size = guts. displaySize;
}
free( monitors);
}
if ( window_state != wsMaximized) {
XX-> zoomRect. right = XX-> size. x;
XX-> zoomRect. top = XX-> size. y;
XX-> size. x *= 0.75;
XX-> size. y *= 0.75;
} else {
XX-> flags. zoomed = 1;
NETWM_SET_MAXIMIZED( X_WINDOW, 1);
if ( net_supports_maximization()) {
XX-> zoomRect. right = XX-> size. x;
XX-> zoomRect. top = XX-> size. y;
XX-> size. x *= 0.75;
XX-> size. y *= 0.75;
}
}
XX-> origin. x = XX-> origin. y =
XX-> ackOrigin. x = XX-> ackOrigin. y =
XX-> ackSize. x = XX-> ackOrigin. y =
XX-> ackFrameSize. x = XX-> ackFrameSize.y = 0;
bzero( &hints, sizeof( XSizeHints));
hints. flags = PBaseSize;
hints. width = hints. base_width = XX-> size. x;
hints. height = hints. base_height = XX-> size. y;
XSetWMNormalHints( DISP, X_WINDOW, &hints);
XResizeWindow( DISP, XX-> client, XX-> size. x, XX-> size. y);
XResizeWindow( DISP, X_WINDOW, XX-> size. x, XX-> size. y);
TAILQ_INIT( &XX-> configure_pairs);
if (( cep = malloc( sizeof( ConfigureEventPair)))) {
bzero( cep, sizeof( ConfigureEventPair));
cep-> w = XX-> size. x;
cep-> h = XX-> size. y;
TAILQ_INSERT_TAIL( &XX-> configure_pairs, cep, link);
}
prima_send_cmSize( self, p0);
return true;
}
Bool
apc_window_activate( Handle self)
{
DEFXX;
int rev;
XWindow xfoc;
XEvent ev;
if ( !XX->flags. want_visible) return true;
if ( guts. message_boxes) return false;
if ( self && ( self != CApplication( application)-> map_focus( application, self)))
return false;
XMapRaised( DISP, X_WINDOW);
if ( XX-> flags. iconic || XX-> flags. withdrawn)
prima_wm_sync( self, MapNotify);
XGetInputFocus( DISP, &xfoc, &rev);
if ( xfoc == X_WINDOW || xfoc == XX-> client) return true;
XSetInputFocus( DISP, XX-> client, RevertToParent, guts. currentFocusTime);
XCHECKPOINT;
XSync( DISP, false);
while ( XCheckMaskEvent( DISP, FocusChangeMask|ExposureMask, &ev))
prima_handle_event( &ev, nil);
return true;
}
Bool
apc_window_is_active( Handle self)
{
return apc_window_get_active() == self;
}
Bool
apc_window_close( Handle self)
{
return prima_simple_message( self, cmClose, true);
}
Handle
apc_window_get_active( void)
{
Handle x = guts. focused;
while ( x && !X(x)-> type. window) x = PWidget(x)-> owner;
return x;
}
int
apc_window_get_border_icons( Handle self)
{
return X(self)-> borderIcons;
}
int
apc_window_get_border_style( Handle self)
{
return X(self)-> flags. sizeable ? bsSizeable : bsDialog;
}
ApiHandle
apc_window_get_client_handle( Handle self)
{
return X(self)-> client;
}
Point
apc_window_get_client_pos( Handle self)
{
if ( !X(self)-> flags. configured) prima_wm_sync( self, ConfigureNotify);
return X(self)-> origin;
}
Point
apc_window_get_client_size( Handle self)
{
if ( !X(self)-> flags. configured) prima_wm_sync( self, ConfigureNotify);
return X(self)-> size;
}
Bool
apc_window_get_icon( Handle self, Handle icon)
{
XWMHints * hints;
Pixmap xor, and;
unsigned int xx, xy, ax, ay, xd, ad;
Bool ret;
if ( !icon)
return X(self)-> flags. has_icon ? true : false;
else
if ( !X(self)-> flags. has_icon) return false;
if ( !( hints = XGetWMHints( DISP, X_WINDOW))) return false;
if ( !icon || !hints-> icon_pixmap) {
Bool ret = hints-> icon_pixmap != nilHandle;
XFree( hints);
return ret;
}
xor = hints-> icon_pixmap;
and = hints-> icon_mask;
XFree( hints);
{
XWindow foo;
unsigned int bar;
int bar2;
if ( !XGetGeometry( DISP, xor, &foo, &bar2, &bar2, &xx, &xy, &bar, &xd))
return false;
if ( and && (!XGetGeometry( DISP, and, &foo, &bar2, &bar2, &ax, &ay, &bar, &ad)))
return false;
}
CImage( icon)-> create_empty( icon, xx, xy, ( xd == 1) ? 1 : guts. qdepth);
if ( !prima_std_query_image( icon, xor)) return false;
if ( and) {
HV * profile = newHV();
Handle mask = Object_create( "Prima::Image", profile);
sv_free(( SV *) profile);
CImage( mask)-> create_empty( mask, ax, ay, ( ad == 1) ? imBW : guts. qdepth);
ret = prima_std_query_image( mask, and);
if (( PImage( mask)-> type & imBPP) != 1)
CImage( mask)-> type( mask, true, imBW);
if ( ret) {
int i;
Byte *d = PImage(mask)-> data;
for ( i = 0; i < PImage(mask)-> dataSize; i++, d++)
*d = ~(*d);
} else
bzero( PImage( mask)-> data, PImage( mask)-> dataSize);
if ( xx != ax || xy != ay) {
Point p;
p.x = xx;
p.y = xy;
CImage( mask)-> size( mask, true, p);
}
memcpy( PIcon( icon)-> mask, PImage( mask)-> data, PIcon( icon)-> maskSize);
Object_destroy( mask);
}
return true;
}
int
apc_window_get_window_state( Handle self)
{
return (X(self)-> flags. iconic != 0) ? wsMinimized :
((X(self)-> flags. zoomed != 0) ? wsMaximized : wsNormal);
}
Bool
apc_window_get_task_listed( Handle self)
{
return X(self)-> flags. task_listed;
}
Bool
apc_window_set_caption( Handle self, const char *caption, Bool utf8)
{
XTextProperty p;
if ( utf8) {
if ( XStringListToTextProperty(( char **) &caption, 1, &p) != 0) {
XSetWMIconName( DISP, X_WINDOW, &p);
XSetWMName( DISP, X_WINDOW, &p);
XFree( p. value);
}
XChangeProperty( DISP, X_WINDOW, NET_WM_NAME, UTF8_STRING, 8,
PropModeReplace, ( unsigned char*) caption, strlen( caption) + 1);
XChangeProperty( DISP, X_WINDOW, NET_WM_ICON_NAME, UTF8_STRING, 8,
PropModeReplace, ( unsigned char*) caption, strlen( caption) + 1);
} else {
XDeleteProperty( DISP, X_WINDOW, NET_WM_NAME);
XDeleteProperty( DISP, X_WINDOW, NET_WM_ICON_NAME);
if ( XStringListToTextProperty(( char **) &caption, 1, &p) != 0) {
XSetWMIconName( DISP, X_WINDOW, &p);
XSetWMName( DISP, X_WINDOW, &p);
XFree( p. value);
}
}
XFlush( DISP);
return true;
}
XWindow
prima_find_frame_window( XWindow w)
{
XWindow r, p, *c;
unsigned int nc;
if ( w == None)
return None;
while ( XQueryTree( DISP, w, &r, &p, &c, &nc)) {
if (c)
XFree(c);
if ( p == r)
return w;
w = p;
}
return None;
}
Bool
prima_get_frame_info( Handle self, PRect r)
{
DEFXX;
XWindow p, dummy;
int px, py;
unsigned int pw, ph, pb, pd;
bzero( r, sizeof( Rect));
p = prima_find_frame_window( X_WINDOW);
if ( p == nilHandle) {
r-> left = XX-> decorationSize. x;
r-> top = XX-> decorationSize. y;
} else if ( p != X_WINDOW)
if ( !XTranslateCoordinates( DISP, X_WINDOW, p, 0, 0, &r-> left, &r-> bottom, &dummy))
warn( "error in XTranslateCoordinates()");
if ( !XGetGeometry( DISP, p, &dummy, &px, &py, &pw, &ph, &pb, &pd)) {
warn( "error in XGetGeometry()");
r-> right = pw - r-> left - XX-> size. x;
r-> top = ph - r-> right - XX-> size. y;
}
r-> top += XX-> menuHeight;
return true;
}
void
apc_SetWMNormalHints( Handle self, XSizeHints * hints)
{
DEFXX;
hints-> flags |= PMinSize | PMaxSize;
if ( XX-> flags. sizeable) {
int h = PWidget(self)-> sizeMin.y;
if ( h == 0) h = 1;
hints-> min_width = PWidget(self)-> sizeMin.x;
hints-> min_height = h + XX-> menuHeight;
hints-> max_width = PWidget(self)-> sizeMax.x;
hints-> max_height = PWidget(self)-> sizeMax.y + XX-> menuHeight;
if ( !XX-> flags. sizemax_set &&
PWidget(self)-> sizeMax.x == 16384 &&
PWidget(self)-> sizeMax.y == 16384) {
hints-> flags &= ~ PMaxSize;
}
else
XX-> flags. sizemax_set = 1;
} else {
Point who;
who. x = ( hints-> flags & USSize) ? hints-> width : XX-> size. x;
who. y = ( hints-> flags & USSize) ? hints-> height : XX-> size. y + XX-> menuHeight;
hints-> min_width = who. x;
hints-> min_height = who. y;
hints-> max_width = who. x;
hints-> max_height = who. y;
XX-> flags. sizemax_set = 1;
}
XSetWMNormalHints( DISP, X_WINDOW, hints);
XCHECKPOINT;
}
Bool
apc_window_set_client_pos( Handle self, int x, int y)
{
DEFXX;
XSizeHints hints;
bzero( &hints, sizeof( XSizeHints));
if ( XX-> flags. zoomed) {
XX-> zoomRect. left = x;
XX-> zoomRect. bottom = y;
return true;
}
if ( x == XX-> origin. x && y == XX-> origin. y) return true;
XX-> flags. position_determined = 1;
if ( XX-> client == guts. grab_redirect) {
XWindow rx;
XTranslateCoordinates( DISP, XX-> client, guts. root, 0, 0,
&guts. grab_translate_mouse.x, &guts. grab_translate_mouse.y, &rx);
}
y = guts. displaySize.y - XX-> size.y - XX-> menuHeight - y;
hints. flags = USPosition;
hints. x = x - XX-> decorationSize. x;
hints. y = y - XX-> decorationSize. y;
XMoveWindow( DISP, X_WINDOW, hints. x, hints. y);
prima_wm_sync( self, ConfigureNotify);
return true;
}
static void
apc_window_set_rect( Handle self, int x, int y, int szx, int szy)
{
DEFXX;
XSizeHints hints;
Point psize = XX-> size;
ConfigureEventPair *cep;
bzero( &hints, sizeof( XSizeHints));
hints. flags = USPosition | USSize;
hints. x = x - XX-> decorationSize. x;
hints. y = guts. displaySize. y - szy - XX-> menuHeight - y - XX-> decorationSize. y;
hints. width = szx;
hints. height = szy + XX-> menuHeight;
XX-> flags. position_determined = 1;
XX-> size. x = szx;
XX-> size. y = szy;
XMoveResizeWindow( DISP, XX-> client, 0, XX-> menuHeight, hints. width, hints. height - XX-> menuHeight);
XMoveResizeWindow( DISP, X_WINDOW, hints. x, hints. y, hints. width, hints. height);
if (( cep = malloc( sizeof( ConfigureEventPair)))) {
bzero( cep, sizeof( ConfigureEventPair));
cep-> w = hints. width;
cep-> h = hints. height;
TAILQ_INSERT_TAIL( &XX-> configure_pairs, cep, link);
}
apc_SetWMNormalHints( self, &hints);
prima_send_cmSize( self, psize);
if ( PObject( self)-> stage == csDead) return;
prima_wm_sync( self, ConfigureNotify);
}
static Bool
window_set_client_size( Handle self, int width, int height)
{
DEFXX;
XSizeHints hints;
PWidget widg = PWidget( self);
Bool implicit_move = false;
Point post, psize;
ConfigureEventPair *cep;
widg-> virtualSize. x = width;
widg-> virtualSize. y = height;
width = ( width >= widg-> sizeMin. x)
? (( width <= widg-> sizeMax. x)
? width
: widg-> sizeMax. x)
: widg-> sizeMin. x;
if ( width == 0) width = 1;
height = ( height >= widg-> sizeMin. y)
? (( height <= widg-> sizeMax. y)
? height
: widg-> sizeMax. y)
: widg-> sizeMin. y;
if ( height == 0) height = 1;
if ( XX-> flags. zoomed) {
XX-> zoomRect. right = width;
XX-> zoomRect. top = height;
return true;
}
bzero( &hints, sizeof( XSizeHints));
hints. flags = USSize | ( XX-> flags. position_determined ? USPosition : 0);
post = XX-> origin;
psize = XX-> size;
hints. x = XX-> origin. x - XX-> decorationSize. x;
hints. y = guts. displaySize.y - height - XX-> menuHeight - XX-> origin. y - XX-> decorationSize.y;
hints. width = width;
hints. height = height + XX-> menuHeight;
XX-> size. x = width;
XX-> size. y = height;
apc_SetWMNormalHints( self, &hints);
XMoveResizeWindow( DISP, XX-> client, 0, XX-> menuHeight, width, height);
if ( XX-> flags. position_determined) {
XMoveResizeWindow( DISP, X_WINDOW, hints. x, hints. y, width, height + XX-> menuHeight);
implicit_move = true;
} else {
XResizeWindow( DISP, X_WINDOW, width, height + XX-> menuHeight);
}
XCHECKPOINT;
prima_send_cmSize( self, psize);
if ( PObject( self)-> stage == csDead) return false;
prima_wm_sync( self, ConfigureNotify);
if ( implicit_move && (( XX-> origin.x != post.x) || (XX-> origin.y != post.y))) {
XX-> decorationSize. x = XX-> origin.x - post. x;
XX-> decorationSize. y = - XX-> origin.y + post. y;
}
if (( cep = malloc( sizeof( ConfigureEventPair)))) {
bzero( cep, sizeof( ConfigureEventPair));
cep-> w = hints. width;
cep-> h = hints. height;
TAILQ_INSERT_TAIL( &XX-> configure_pairs, cep, link);
}
return true;
}
Bool
apc_window_set_client_rect( Handle self, int x, int y, int width, int height)
{
DEFXX;
PWidget widg = PWidget( self);
widg-> virtualSize. x = width;
widg-> virtualSize. y = height;
width = ( width >= widg-> sizeMin. x)
? (( width <= widg-> sizeMax. x)
? width
: widg-> sizeMax. x)
: widg-> sizeMin. x;
if ( width == 0) width = 1;
height = ( height >= widg-> sizeMin. y)
? (( height <= widg-> sizeMax. y)
? height
: widg-> sizeMax. y)
: widg-> sizeMin. y;
if ( height == 0) height = 1;
if ( XX-> flags. zoomed) {
XX-> zoomRect. left = x;
XX-> zoomRect. bottom = y;
XX-> zoomRect. right = width;
XX-> zoomRect. top = height;
return true;
}
if ( x == XX-> origin. x && y == XX-> origin. y &&
width == XX-> size. x && height == XX-> size. y ) return true;
apc_window_set_rect( self, x, y, width, height);
return true;
}
Bool
apc_window_set_client_size( Handle self, int width, int height)
{
DEFXX;
if ( width == XX-> size. x && height == XX-> size. y) return true;
return window_set_client_size( self, width, height);
}
Bool
prima_window_reset_menu( Handle self, int newMenuHeight)
{
DEFXX;
int ret = true;
if ( newMenuHeight != XX-> menuHeight) {
int oh = XX-> menuHeight;
XX-> menuHeight = newMenuHeight;
if ( PWindow(self)-> stage <= csNormal)
ret = window_set_client_size( self, XX-> size.x, XX-> size.y);
else
XX-> size. y -= newMenuHeight - oh;
#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
if ( XX-> shape_extent. x != 0 || XX-> shape_extent. y != 0) {
int ny = XX-> size. y + XX-> menuHeight - XX-> shape_extent. y;
if ( XX-> shape_offset. y != ny) {
XShapeOffsetShape( DISP, X_WINDOW, ShapeBounding, 0, ny - XX-> shape_offset. y);
XX-> shape_offset. y = ny;
}
}
#endif
}
return ret;
}
Bool
apc_window_set_visible( Handle self, Bool show)
{
DEFXX;
if ( show) {
if ( XX-> flags. mapped) return true;
} else {
if ( !XX-> flags. mapped) return true;
}
XX-> flags. want_visible = show;
if ( show) {
Bool iconic = XX-> flags. iconic;
if ( XX-> flags. withdrawn) {
XWMHints wh;
wh. initial_state = iconic ? IconicState : NormalState;
wh. flags = StateHint;
XSetWMHints( DISP, X_WINDOW, &wh);
XX-> flags. withdrawn = 0;
}
XMapWindow( DISP, X_WINDOW);
XX-> flags. iconic = iconic;
prima_wm_sync( self, MapNotify);
} else {
if ( XX-> flags. iconic) {
XWithdrawWindow( DISP, X_WINDOW, SCREEN);
XX-> flags. withdrawn = 1;
} else
XUnmapWindow( DISP, X_WINDOW);
prima_wm_sync( self, UnmapNotify);
}
XCHECKPOINT;
return true;
}
/* apc_window_set_menu is in apc_menu.c */
Bool
apc_window_set_icon( Handle self, Handle icon)
{
DEFXX;
PIcon i = ( PIcon) icon;
XIconSize * sz = nil;
Pixmap xor, and;
XWMHints wmhints;
int n;
if ( !icon || i-> w == 0 || i-> h == 0) {
if ( !XX-> flags. has_icon) return true;
XX-> flags. has_icon = false;
XDeleteProperty( DISP, X_WINDOW, XA_WM_HINTS);
wmhints. flags = InputHint;
wmhints. input = false;
XSetWMHints( DISP, X_WINDOW, &wmhints);
return true;
}
if ( XGetIconSizes( DISP, guts.root, &sz, &n) && n > 0) {
int zx = sz-> min_width, zy = sz-> min_height;
while ( 1) {
if ( i-> w <= zx || i-> h <= zy) break;
zx += sz-> width_inc;
zy += sz-> height_inc;
if ( zx >= sz-> max_width || zy >= sz-> max_height) break;
}
if ( zx > sz-> max_width) zx = sz-> max_width;
if ( zy > sz-> max_height) zy = sz-> max_height;
if (( zx != i-> w && zy != i-> h) || ( sz-> max_width != i-> w && sz-> max_height != i-> h)) {
Point z;
i = ( PIcon) i-> self-> dup( icon);
z.x = zx;
z.y = zy;
i-> self-> size(( Handle) i, true, z);
}
XFree( sz);
}
xor = prima_std_pixmap( icon, CACHE_LOW_RES);
if ( !xor) goto FAIL;
{
GC gc;
XGCValues gcv;
and = XCreatePixmap( DISP, guts. root, i-> w, i-> h, 1);
if ( !and) {
XFreePixmap( DISP, xor);
goto FAIL;
}
gcv. graphics_exposures = false;
gc = XCreateGC( DISP, and, GCGraphicsExposures, &gcv);
if ( X(icon)-> image_cache. icon) {
XSetBackground( DISP, gc, 0xffffffff);
XSetForeground( DISP, gc, 0x00000000);
prima_put_ximage( and, gc, X(icon)-> image_cache. icon, 0, 0, 0, 0, i-> w, i-> h);
} else {
XSetForeground( DISP, gc, 0xffffffff);
XFillRectangle( DISP, and, gc, 0, 0, i-> w + 1, i-> h + 1);
}
XFreeGC( DISP, gc);
}
if (( Handle) i != icon) Object_destroy(( Handle) i);
wmhints. flags = InputHint | IconPixmapHint | IconMaskHint;
wmhints. icon_pixmap = xor;
wmhints. icon_mask = and;
wmhints. input = false;
XSetWMHints( DISP, X_WINDOW, &wmhints);
XCHECKPOINT;
XX-> flags. has_icon = true;
return true;
FAIL:
if (( Handle) i != icon) Object_destroy(( Handle) i);
return false;
}
Bool
apc_window_set_window_state( Handle self, int state)
{
DEFXX;
Event e;
int sync = 0, did_net_zoom = 0;
switch ( state) {
case wsMinimized:
if ( XX-> flags. iconic) return false;
break;
case wsMaximized:
if ( XX-> flags. zoomed) return false;
break;
case wsNormal:
if ( !XX-> flags. iconic && !XX-> flags. zoomed) return false;
break;
default:
return false;
}
/* operate via NET_WM */
if ( state == wsMaximized && !XX-> flags. zoomed && net_supports_maximization()) {
Bool visible = XX-> flags. mapped;
Rect zoomRect;
NETWM_SET_MAXIMIZED( X_WINDOW, 1);
zoomRect. left = XX-> origin.x;
zoomRect. bottom = XX-> origin.y;
zoomRect. right = XX-> size.x;
zoomRect. top = XX-> size.y;
if ( visible) {
prima_wm_sync( self, ConfigureNotify);
if ( !prima_wm_net_state_read_maximization( X_WINDOW, NET_WM_STATE)) {
/* wm denies maximization request, or we lost in the race ( see above ),
do maximization by casual heuristic */
goto FALL_THROUGH;
}
}
XX-> zoomRect = zoomRect; /* often reset in ConfigureNotify to already maximized window */
XX-> flags. zoomed = 1;
did_net_zoom = 1;
sync = 0;
FALL_THROUGH:;
}
if ( !XX-> flags. withdrawn) {
if ( state == wsMinimized) {
XIconifyWindow( DISP, X_WINDOW, SCREEN);
if ( XX-> flags. mapped) sync = UnmapNotify;
} else {
XMapWindow( DISP, X_WINDOW);
if ( !XX-> flags. mapped && !did_net_zoom) sync = MapNotify;
}
}
XX-> flags. iconic = ( state == wsMinimized) ? 1 : 0;
if ( state == wsMaximized && !XX-> flags. zoomed && !did_net_zoom) {
int dx = ( XX-> decorationSize. x > 0 ) ? XX-> decorationSize. x : 2;
int dy = ( XX-> decorationSize. y > 0 ) ? XX-> decorationSize. y : 20;
XX-> zoomRect. left = XX-> origin.x;
XX-> zoomRect. bottom = XX-> origin.y;
XX-> zoomRect. right = XX-> size.x;
XX-> zoomRect. top = XX-> size.y;
apc_window_set_rect( self, dx * 2, dy * 2,
guts. displaySize.x - dx * 4, guts. displaySize. y - XX-> menuHeight - dy * 4);
if ( !XX-> flags. zoomed) sync = ConfigureNotify;
XX-> flags. zoomed = 1;
}
if ( XX-> flags. zoomed && state != wsMaximized) {
NETWM_SET_MAXIMIZED( X_WINDOW, 0);
apc_window_set_rect( self, XX-> zoomRect. left, XX-> zoomRect. bottom,
XX-> zoomRect. right, XX-> zoomRect. top);
if ( XX-> flags. zoomed) sync = ConfigureNotify;
XX-> flags. zoomed = 0;
}
bzero( &e, sizeof(e));
e. gen. source = self;
e. cmd = cmWindowState;
e. gen. i = state;
apc_message( self, &e, false);
if ( sync) prima_wm_sync( self, sync);
return true;
}
static Bool
window_start_modal( Handle self, Bool shared, Handle insert_before)
{
DEFXX;
Handle selectee;
if ( guts. grab_widget)
apc_widget_set_capture( guts. grab_widget, 0, 0);
if (( XX-> preexec_focus = apc_widget_get_focused()))
protect_object( XX-> preexec_focus);
CWindow( self)-> exec_enter_proc( self, shared, insert_before);
apc_widget_set_enabled( self, true);
apc_widget_set_visible( self, true);
apc_window_activate( self);
selectee = CWindow(self)->get_selectee( self);
if ( selectee && selectee != self) Widget_selected( selectee, true, true);
prima_simple_message( self, cmExecute, true);
guts. modal_count++;
return true;
}
Bool
apc_window_execute( Handle self, Handle insert_before)
{
DEFXX;
Handle toplevel;
if (!application) return false;
toplevel = CApplication(application)-> get_modal_window(application, mtExclusive, true);
if ( toplevel == nilHandle) {
if (
PWindow(self)-> owner &&
PWindow(self)-> owner != application
)
toplevel = PWindow(self)-> owner;
}
/* find main window */
if ( toplevel == nilHandle) {
int i;
PList l = & PWidget(application)-> widgets;
for ( i = 0; i < l-> count; i++)
if ( PObject(l-> items[i])-> options. optMainWindow && self != l->items[i]) {
toplevel = l-> items[i];
break;
}
}
if ( toplevel) XSetTransientForHint( DISP, X_WINDOW, PWidget(toplevel)-> handle);
XX-> flags.modal = true;
NETWM_SET_MODAL( X_WINDOW, XX-> flags.modal);
if ( !window_start_modal( self, false, insert_before))
return false;
protect_object( self);
XSync( DISP, false);
while ( prima_one_loop_round( true, true) && XX-> flags.modal)
;
if ( toplevel) XSetTransientForHint( DISP, X_WINDOW, None);
if ( X_WINDOW) NETWM_SET_MODAL( X_WINDOW, XX-> flags.modal);
unprotect_object( self);
return true;
}
Bool
apc_window_execute_shared( Handle self, Handle insert_before)
{
return window_start_modal( self, true, insert_before);
}
Bool
apc_window_end_modal( Handle self)
{
PWindow win = PWindow(self);
Handle modal, oldfoc;
DEFXX;
XX-> flags.modal = false;
CWindow( self)-> exec_leave_proc( self);
apc_widget_set_visible( self, false);
if ( application) {
modal = CApplication(application)->popup_modal( application);
if ( !modal && win->owner)
CWidget( win->owner)-> set_selected( win->owner, true);
if (( oldfoc = XX-> preexec_focus)) {
if ( PWidget( oldfoc)-> stage == csNormal)
CWidget( oldfoc)-> set_focused( oldfoc, true);
unprotect_object( oldfoc);
}
}
if ( guts. modal_count > 0)
guts. modal_count--;
return true;
}
Bool
apc_window_get_on_top( Handle self)
{
Atom type;
long * prop;
int format;
unsigned long i, n, left;
Bool on_top = 0;
if ( guts. icccm_only) return false;
if ( XGetWindowProperty( DISP, X_WINDOW, NET_WM_STATE, 0, 32, false, XA_ATOM,
&type, &format, &n, &left, (unsigned char**)&prop) == Success) {
if ( prop) {
for ( i = 0; i < n; i++) {
if (
prop[i] == NET_WM_STATE_STAYS_ON_TOP ||
prop[i] == NET_WM_STATE_ABOVE
) {
on_top = 1;
break;
}
}
XFree(( unsigned char *) prop);
}
}
return on_top;
}