f1ea238aa1
As noticed by Tristan Le Guern. okay matthieu@
2006 lines
47 KiB
C
2006 lines
47 KiB
C
/*
|
|
FvwmButtons v2.0.41-plural-Z-alpha, copyright 1996, Jarl Totland
|
|
|
|
* This module, and the entire GoodStuff program, and the concept for
|
|
* interfacing this module to the Window Manager, are all original work
|
|
* by Robert Nation
|
|
*
|
|
* Copyright 1993, Robert Nation. No guarantees or warantees or anything
|
|
* are provided or implied in any way whatsoever. Use this program at your
|
|
* own risk. Permission to use this program for any purpose is given,
|
|
* as long as the copyright is kept intact.
|
|
|
|
*/
|
|
|
|
/* ------------------------------- includes -------------------------------- */
|
|
#include "config.h"
|
|
|
|
#ifdef HAVE_SYS_BSDTYPES_H
|
|
#include <sys/bsdtypes.h> /* Saul */
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
|
|
#include <X11/keysym.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xproto.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Intrinsic.h>
|
|
#ifdef XPM
|
|
#include <X11/xpm.h>
|
|
#endif
|
|
#ifdef SHAPE
|
|
#include <X11/extensions/shape.h>
|
|
#endif
|
|
|
|
#include "../../fvwm/module.h"
|
|
#include "../../libs/fvwmlib.h"
|
|
#include "FvwmButtons.h"
|
|
#include "misc.h" /* ConstrainSize() */
|
|
#include "parse.h" /* ParseOptions() */
|
|
#include "icons.h" /* CreateIconWindow(), ConfigureIconWindow() */
|
|
#include "draw.h"
|
|
|
|
|
|
#define MW_EVENTS (ExposureMask |\
|
|
StructureNotifyMask |\
|
|
ButtonReleaseMask | ButtonPressMask |\
|
|
KeyReleaseMask | KeyPressMask)
|
|
/* SW_EVENTS are for swallowed windows... */
|
|
#define SW_EVENTS (PropertyChangeMask | StructureNotifyMask |\
|
|
ResizeRedirectMask | SubstructureNotifyMask)
|
|
|
|
#ifdef DEBUG_FVWM
|
|
#define MySendText(a,b,c) {\
|
|
fprintf(stderr,"%s: Sending text to fvwm: \"%s\"\n",MyName,(b));\
|
|
SendText((a),(b),(c));}
|
|
#else
|
|
#define MySendText(a,b,c) SendText((a),(b),(c));
|
|
#endif
|
|
|
|
/* --------------------------- external functions -------------------------- */
|
|
extern void DumpButtons(button_info*);
|
|
extern void SaveButtons(button_info*);
|
|
|
|
/* ------------------------------ prototypes ------------------------------- */
|
|
|
|
void DeadPipe(int nonsense) __attribute__((__noreturn__));
|
|
static void DeadPipeCleanup(void);
|
|
static RETSIGTYPE TerminateHandler(int sig);
|
|
void SetButtonSize(button_info*,int,int);
|
|
/* main */
|
|
void Loop(void);
|
|
void RedrawWindow(button_info*);
|
|
void RecursiveLoadData(button_info*,int*,int*);
|
|
void CreateWindow(button_info*,int,int);
|
|
void nocolor(const char *a, const char *b) __attribute__((__noreturn__));
|
|
Pixel GetColor(char *name);
|
|
int My_XNextEvent(Display *dpy, XEvent *event);
|
|
void process_message(unsigned long type,unsigned long *body);
|
|
extern void send_clientmessage (Display *disp, Window w, Atom a, Time timestamp);
|
|
void CheckForHangon(unsigned long*);
|
|
Window GetRealGeometry(Display*,Window,int*,int*,ushort*,ushort*,
|
|
ushort*,ushort*);
|
|
void swallow(unsigned long*);
|
|
void AddButtonAction(button_info*,int,char*);
|
|
char *GetButtonAction(button_info*,int);
|
|
|
|
void DebugEvents(XEvent*);
|
|
|
|
panel_info *seekpanel(button_info *);
|
|
void Slide(panel_info *, button_info *);
|
|
|
|
/* -------------------------------- globals ---------------------------------*/
|
|
|
|
Display *Dpy;
|
|
Window Root;
|
|
Window MyWindow;
|
|
char *MyName;
|
|
XFontStruct *font;
|
|
int screen;
|
|
int d_depth;
|
|
|
|
int x_fd,fd_width;
|
|
|
|
char *config_file = NULL;
|
|
|
|
static Atom _XA_WM_DEL_WIN;
|
|
Atom _XA_WM_PROTOCOLS;
|
|
Atom _XA_WM_NORMAL_HINTS;
|
|
Atom _XA_WM_NAME;
|
|
|
|
char *iconPath = NULL;
|
|
char *pixmapPath = NULL;
|
|
|
|
Pixel hilite_pix, back_pix, shadow_pix, fore_pix;
|
|
GC NormalGC;
|
|
int Width,Height;
|
|
|
|
int x= -30000,y= -30000,w= -1,h= -1,gravity = NorthWestGravity;
|
|
int new_desk = 0;
|
|
int ready = 0;
|
|
int xneg = 0, yneg = 0;
|
|
|
|
button_info *CurrentButton = NULL;
|
|
int fd[2];
|
|
|
|
button_info *UberButton=NULL;
|
|
|
|
panel_info *MainPanel = NULL, *CurrentPanel = NULL, *PanelIndex;
|
|
int dpw, dph;
|
|
|
|
int save_color_limit; /* Color limit, if any */
|
|
|
|
static volatile sig_atomic_t isTerminated = False;
|
|
/* ------------------------------ Misc functions ----------------------------*/
|
|
|
|
#ifdef DEBUG
|
|
char *mymalloc(int length)
|
|
{
|
|
int i=length;
|
|
char *p=safemalloc(length);
|
|
while(i)
|
|
p[--i]=255;
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
*** Some fancy routines straight out of the manual :-) Used in DeadPipe.
|
|
**/
|
|
Bool DestroyedWindow(Display *d,XEvent *e,char *a)
|
|
{
|
|
if(e->xany.window == (Window)a)
|
|
if((e->type == DestroyNotify && e->xdestroywindow.window == (Window)a)||
|
|
(e->type == UnmapNotify && e->xunmap.window == (Window)a))
|
|
return True;
|
|
return False;
|
|
}
|
|
int IsThereADestroyEvent(button_info *b)
|
|
{
|
|
XEvent event;
|
|
Bool DestroyedWindow();
|
|
return XCheckIfEvent(Dpy,&event,DestroyedWindow,(char*)b->IconWin);
|
|
}
|
|
|
|
/**
|
|
*** DeadPipe()
|
|
*** Externally callable function to quit! Note that DeadPipeCleanup
|
|
*** is an exit-procedure and so will be called automatically
|
|
**/
|
|
void DeadPipe(int whatever)
|
|
{
|
|
exit(0);
|
|
}
|
|
|
|
/**
|
|
*** TerminateHandler()
|
|
*** Signal handler that will make the event-loop terminate
|
|
**/
|
|
static RETSIGTYPE
|
|
TerminateHandler(int sig)
|
|
{
|
|
isTerminated = True;
|
|
}
|
|
|
|
/**
|
|
*** DeadPipeCleanup()
|
|
*** Remove all the windows from the Button-Bar, and close them as necessary
|
|
**/
|
|
static void DeadPipeCleanup(void)
|
|
{
|
|
button_info *b,*ub=UberButton;
|
|
int button=-1;
|
|
|
|
signal(SIGPIPE, SIG_IGN);/* Xsync may cause SIGPIPE */
|
|
|
|
XSync(Dpy,0); /* Wait for thing to settle down a bit */
|
|
XGrabServer(Dpy); /* We don't want interference right now */
|
|
while(NextButton(&ub,&b,&button,0))
|
|
{
|
|
/* delete swallowed windows */
|
|
if((buttonSwallowCount(b)==3) && b->IconWin)
|
|
{
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"%s: Button 0x%06x window 0x%x (\"%s\") is ",
|
|
MyName,(ushort)b,(ushort)b->IconWin,b->hangon);
|
|
# endif
|
|
if(!IsThereADestroyEvent(b)) { /* Has someone destroyed it? */
|
|
if(!(buttonSwallow(b)&b_NoClose))
|
|
{
|
|
if(buttonSwallow(b)&b_Kill)
|
|
{
|
|
XKillClient(Dpy,b->IconWin);
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"now killed\n");
|
|
# endif
|
|
}
|
|
else
|
|
{
|
|
send_clientmessage(Dpy,b->IconWin,_XA_WM_DEL_WIN,
|
|
CurrentTime);
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"now deleted\n");
|
|
# endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"now unswallowed\n");
|
|
# endif
|
|
XReparentWindow(Dpy,b->IconWin,Root,b->x,b->y);
|
|
XMoveWindow(Dpy,b->IconWin,b->x,b->y);
|
|
XResizeWindow(Dpy,b->IconWin,b->w,b->h);
|
|
XSetWindowBorderWidth(Dpy,b->IconWin,b->bw);
|
|
}
|
|
}
|
|
# ifdef DEBUG_HANGON
|
|
else
|
|
fprintf(stderr,"already handled\n");
|
|
# endif
|
|
}
|
|
}
|
|
XUngrabServer(Dpy); /* We're through */
|
|
XSync(Dpy,0); /* Let it all die down again so we can catch our X errors... */
|
|
|
|
/* Hey, we have to free the pictures too! */
|
|
button=-1;ub=UberButton;
|
|
while(NextButton(&ub,&b,&button,1))
|
|
{
|
|
if(b->flags&b_Icon)
|
|
DestroyPicture(Dpy,b->icon);
|
|
if(b->flags&b_IconBack)
|
|
DestroyPicture(Dpy,b->backicon);
|
|
if(b->flags&b_Container && b->c->flags&b_IconBack
|
|
&& !(b->c->flags&b_TransBack))
|
|
DestroyPicture(Dpy,b->c->backicon);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*** SetButtonSize()
|
|
*** Propagates global geometry down through the buttonhierarchy.
|
|
**/
|
|
void SetButtonSize(button_info *ub,int w,int h)
|
|
{
|
|
int i=0,dx,dy;
|
|
if(!ub || !(ub->flags&b_Container))
|
|
{
|
|
fprintf(stderr,"%s: BUG: Tried to set size of noncontainer\n",MyName);
|
|
exit(2);
|
|
}
|
|
if(ub->c->num_rows==0 || ub->c->num_columns==0)
|
|
{
|
|
fprintf(stderr,"%s: BUG: Set size when rows/cols was unset\n",MyName);
|
|
exit(2);
|
|
}
|
|
w*=ub->BWidth;
|
|
h*=ub->BHeight;
|
|
|
|
if(ub->parent)
|
|
{
|
|
i=buttonNum(ub);
|
|
ub->c->xpos=buttonXPos(ub,i);
|
|
ub->c->ypos=buttonYPos(ub,i);
|
|
}
|
|
dx=buttonXPad(ub)+buttonFrame(ub);
|
|
dy=buttonYPad(ub)+buttonFrame(ub);
|
|
ub->c->xpos+=dx;
|
|
ub->c->ypos+=dy;
|
|
w-=2*dx;
|
|
h-=2*dy;
|
|
ub->c->ButtonWidth=w/ub->c->num_columns;
|
|
ub->c->ButtonHeight=h/ub->c->num_rows;
|
|
|
|
i=0;
|
|
while(i<ub->c->num_buttons)
|
|
{
|
|
if(ub->c->buttons[i] && ub->c->buttons[i]->flags&b_Container)
|
|
SetButtonSize(ub->c->buttons[i],
|
|
ub->c->ButtonWidth,ub->c->ButtonHeight);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*** AddButtonAction()
|
|
**/
|
|
void AddButtonAction(button_info *b,int n,char *action)
|
|
{
|
|
int l;
|
|
char *s;
|
|
char *t;
|
|
|
|
if(!b || n<0 || n>3 || !action)
|
|
{
|
|
fprintf(stderr,"%s: BUG: AddButtonAction failed\n",MyName);
|
|
exit(2);
|
|
}
|
|
if(b->flags&b_Action)
|
|
{
|
|
if(b->action[n])
|
|
free(b->action[n]);
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
b->action=(char**)mymalloc(4*sizeof(char*));
|
|
for(i=0;i<4;b->action[i++]=NULL);
|
|
b->flags|=b_Action;
|
|
}
|
|
|
|
while (*action && isspace(*action))
|
|
action++;
|
|
l = strlen(action);
|
|
if (l > 1)
|
|
{
|
|
switch (action[0])
|
|
{
|
|
case '\"':
|
|
case '\'':
|
|
case '`':
|
|
s = SkipQuote(action, NULL, "", "");
|
|
/* Strip outer quotes */
|
|
if (*s == 0)
|
|
{
|
|
action++;
|
|
l -= 2;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
t = (char *)mymalloc(l + 1);
|
|
memmove(t, action, l);
|
|
t[l] = 0;
|
|
b->action[n] = t;
|
|
}
|
|
|
|
/**
|
|
*** GetButtonAction()
|
|
**/
|
|
char *GetButtonAction(button_info *b,int n)
|
|
{
|
|
if(!b || !(b->flags&b_Action) || !(b->action) || n<0 || n>3)
|
|
return NULL;
|
|
return b->action[n];
|
|
}
|
|
|
|
#ifdef SHAPE
|
|
/**
|
|
*** SetTransparentBackground()
|
|
*** use the Shape extension to create a transparent background.
|
|
*** Patrice Fortier
|
|
**/
|
|
void SetTransparentBackground(button_info *ub,int w,int h)
|
|
{
|
|
Pixmap pmap_mask;
|
|
button_info *b;
|
|
GC gc;
|
|
XGCValues gvals;
|
|
unsigned long gcm=0;
|
|
Window root_return;
|
|
int x_return, y_return;
|
|
unsigned int width_return, height_return;
|
|
unsigned int border_width_return;
|
|
unsigned int depth_return;
|
|
int number, i;
|
|
XFontStruct *font;
|
|
|
|
pmap_mask = XCreatePixmap(Dpy,MyWindow,w,h,1);
|
|
gc = XCreateGC(Dpy,pmap_mask,(unsigned long)0,&gvals);
|
|
XSetForeground(Dpy,gc,0);
|
|
XFillRectangle(Dpy,pmap_mask,gc,0,0,w,h);
|
|
XSetForeground(Dpy,gc,1);
|
|
|
|
/*
|
|
* if button has an icon, draw a rect with the same size as the icon
|
|
* (or the mask of the icon),
|
|
* else draw a rect with the same size as the button.
|
|
*/
|
|
|
|
i=-1;
|
|
while(NextButton(&ub,&b,&i,0))
|
|
{
|
|
if(b->flags&b_Icon)
|
|
{
|
|
XGetGeometry(Dpy,b->IconWin,&root_return,&x_return,&y_return,
|
|
&width_return,&height_return,
|
|
&border_width_return,&depth_return);
|
|
|
|
number=buttonNum(b);
|
|
if (b->icon->mask == None)
|
|
XFillRectangle(Dpy,pmap_mask,gc,x_return, y_return,
|
|
b->icon->width,b->icon->height);
|
|
else
|
|
XCopyArea(Dpy,b->icon->mask,pmap_mask,gc,0,0,
|
|
b->icon->width,b->icon->height,x_return,y_return);
|
|
}
|
|
else
|
|
{
|
|
number=buttonNum(b);
|
|
XFillRectangle(Dpy,pmap_mask,gc,buttonXPos(b,number),
|
|
buttonYPos(b,number),
|
|
buttonWidth(b),buttonHeight(b));
|
|
}
|
|
|
|
/* handle button's title */
|
|
font=buttonFont(b);
|
|
if(b->flags&b_Title && font)
|
|
{
|
|
gcm = GCForeground | GCFont;
|
|
gvals.foreground=1;
|
|
gvals.font = font->fid;
|
|
XChangeGC(Dpy,gc,gcm,&gvals);
|
|
DrawTitle(b,pmap_mask,gc);
|
|
}
|
|
}
|
|
XFreeGC(Dpy,gc);
|
|
XShapeCombineMask(Dpy,MyWindow,ShapeBounding,0,0,pmap_mask,ShapeSet);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
*** myErrorHandler()
|
|
*** Shows X errors made by FvwmButtons.
|
|
**/
|
|
XErrorHandler oldErrorHandler=NULL;
|
|
int myErrorHandler(Display *dpy, XErrorEvent *event)
|
|
{
|
|
fprintf(stderr,"%s: Cause of next X Error.\n",MyName);
|
|
/* return (*oldErrorHandler)(dpy,event); */
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------------- main ----------------------------------*/
|
|
|
|
/**
|
|
*** main()
|
|
**/
|
|
int main(int argc, char **argv)
|
|
{
|
|
char *display_name = NULL;
|
|
int i;
|
|
Window root;
|
|
int x,y,maxx,maxy,border_width,depth;
|
|
char *temp, *s;
|
|
button_info *b,*ub;
|
|
|
|
temp=argv[0];
|
|
s=strrchr(argv[0],'/');
|
|
if(s) temp=s+1;
|
|
MyName=mymalloc(strlen(temp)+1);
|
|
strcpy(MyName,temp);
|
|
|
|
#ifdef HAVE_SIGACTION
|
|
{
|
|
struct sigaction sigact;
|
|
|
|
sigemptyset(&sigact.sa_mask);
|
|
# ifdef SA_INTERRUPT
|
|
sigact.sa_flags = SA_INTERRUPT;
|
|
# else
|
|
sigact.sa_flags = 0;
|
|
# endif
|
|
sigact.sa_handler = TerminateHandler;
|
|
|
|
sigaction(SIGPIPE, &sigact, NULL);
|
|
sigaction(SIGINT, &sigact, NULL);
|
|
sigaction(SIGHUP, &sigact, NULL);
|
|
sigaction(SIGQUIT, &sigact, NULL);
|
|
sigaction(SIGTERM, &sigact, NULL);
|
|
}
|
|
#else
|
|
/* We don't have sigaction(), so fall back to less robust methods. */
|
|
signal(SIGPIPE, TerminateHandler);
|
|
signal(SIGINT, TerminateHandler);
|
|
signal(SIGHUP, TerminateHandler);
|
|
signal(SIGQUIT, TerminateHandler);
|
|
signal(SIGTERM, TerminateHandler);
|
|
#endif
|
|
|
|
if(argc<6 || argc>8)
|
|
{
|
|
fprintf(stderr,"%s v%s should only be executed by fvwm!\n",MyName,
|
|
VERSION);
|
|
exit(1);
|
|
}
|
|
|
|
if(argc>6) /* There is a naming argument here! */
|
|
{
|
|
free(MyName);
|
|
MyName=strdup(argv[6]);
|
|
}
|
|
|
|
if(argc>7) /* There is a config file here! */
|
|
{
|
|
config_file=strdup(argv[7]);
|
|
}
|
|
|
|
fd[0]=atoi(argv[1]);
|
|
fd[1]=atoi(argv[2]);
|
|
if (!(Dpy = XOpenDisplay(display_name)))
|
|
{
|
|
fprintf(stderr,"%s: Can't open display %s", MyName,
|
|
XDisplayName(display_name));
|
|
exit (1);
|
|
}
|
|
x_fd=XConnectionNumber(Dpy);
|
|
fd_width=GetFdWidth();
|
|
|
|
screen=DefaultScreen(Dpy);
|
|
Root=RootWindow(Dpy, screen);
|
|
if(Root==None)
|
|
{
|
|
fprintf(stderr,"%s: Screen %d is not valid\n",MyName,screen);
|
|
exit(1);
|
|
}
|
|
d_depth = DefaultDepth(Dpy, screen);
|
|
|
|
oldErrorHandler=XSetErrorHandler(myErrorHandler);
|
|
|
|
UberButton=(button_info*)mymalloc(sizeof(button_info));
|
|
memset(UberButton, 0, sizeof(button_info));
|
|
UberButton->flags=0;
|
|
UberButton->parent=NULL;
|
|
UberButton->BWidth=1;
|
|
UberButton->BHeight=1;
|
|
UberButton->font = NULL;
|
|
UberButton->font_string = NULL;
|
|
MakeContainer(UberButton);
|
|
|
|
dpw = DisplayWidth(Dpy,screen);
|
|
dph = DisplayHeight(Dpy,screen);
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"%s: Parsing...",MyName);
|
|
# endif
|
|
|
|
CurrentPanel = MainPanel
|
|
= (panel_info *) mymalloc(sizeof(panel_info));
|
|
MainPanel->next = NULL;
|
|
MainPanel->uber = UberButton;
|
|
UberButton->title = MyName;
|
|
UberButton->swallow = 1; /* the panel is shown */
|
|
|
|
ParseOptions(UberButton);
|
|
|
|
CurrentPanel = MainPanel; /* reassign CurrentPanel */
|
|
while (CurrentPanel)
|
|
{ UberButton = CurrentPanel->uber;
|
|
|
|
if(UberButton->c->num_buttons==0)
|
|
{
|
|
fprintf(stderr,"%s: No buttons defined. Quitting\n", MyName);
|
|
exit(0);
|
|
}
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"OK\n%s: Shuffling...",MyName);
|
|
# endif
|
|
|
|
ShuffleButtons(UberButton);
|
|
NumberButtons(UberButton);
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"OK\n%s: Loading data...\n",MyName);
|
|
# endif
|
|
|
|
/* Load fonts and icons, calculate max buttonsize */
|
|
maxx=0;maxy=0;
|
|
InitPictureCMap(Dpy,Root); /* store the root cmap */
|
|
RecursiveLoadData(UberButton,&maxx,&maxy);
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"%s: Creating main window...",MyName);
|
|
# endif
|
|
|
|
CreateWindow(UberButton,maxx,maxy);
|
|
|
|
CurrentPanel->uber->IconWinParent = MyWindow;
|
|
CurrentPanel->uber->icon_w = maxx;
|
|
CurrentPanel->uber->icon_h = maxy;
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"OK\n%s: Creating icon windows...",MyName);
|
|
# endif
|
|
|
|
i=-1;ub=UberButton;
|
|
while(NextButton(&ub,&b,&i,0))
|
|
if(b->flags&b_Icon)
|
|
{
|
|
#ifdef DEBUG_INIT
|
|
fprintf(stderr,"0x%06x...",(ushort)b);
|
|
#endif
|
|
CreateIconWindow(b);
|
|
}
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"OK\n%s: Configuring windows...",MyName);
|
|
# endif
|
|
|
|
XGetGeometry(Dpy,MyWindow,&root,&x,&y,(ushort*)&Width,(ushort*)&Height,
|
|
(ushort*)&border_width,(ushort*)&depth);
|
|
SetButtonSize(UberButton,Width,Height);
|
|
i=-1;ub=UberButton;
|
|
while(NextButton(&ub,&b,&i,0))
|
|
ConfigureIconWindow(b);
|
|
|
|
# ifdef SHAPE
|
|
if(UberButton->c->flags&b_TransBack)
|
|
SetTransparentBackground(UberButton,Width,Height);
|
|
# endif
|
|
|
|
i=-1;ub=UberButton;
|
|
while(NextButton(&ub,&b,&i,0))
|
|
MakeButton(b);
|
|
|
|
CurrentPanel = CurrentPanel->next;
|
|
}
|
|
CurrentPanel = MainPanel;
|
|
UberButton = CurrentPanel->uber;
|
|
MyWindow = UberButton->IconWinParent;
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"OK\n%s: Mapping windows...",MyName);
|
|
# endif
|
|
|
|
XMapSubwindows(Dpy,MyWindow);
|
|
XMapWindow(Dpy,MyWindow);
|
|
|
|
SetMessageMask(fd, M_NEW_DESK | M_END_WINDOWLIST | M_MAP | M_WINDOW_NAME |
|
|
M_RES_CLASS | M_CONFIG_INFO | M_END_CONFIG_INFO | M_RES_NAME);
|
|
|
|
/* request a window list, since this triggers a response which
|
|
* will tell us the current desktop and paging status, needed to
|
|
* indent buttons correctly */
|
|
MySendText(fd,"Send_WindowList",0);
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"OK\n%s: Startup complete\n",MyName);
|
|
# endif
|
|
|
|
/*
|
|
** Now that we have finished initialising everything,
|
|
** it is safe(r) to install the clean-up handlers ...
|
|
*/
|
|
atexit(DeadPipeCleanup);
|
|
Loop();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------- Main Loop -------------------------------*/
|
|
|
|
/**
|
|
*** Loop
|
|
**/
|
|
void Loop(void)
|
|
{
|
|
XEvent Event;
|
|
KeySym keysym;
|
|
char buffer[10],*tmp,*act;
|
|
int i,i2,button;
|
|
button_info *ub,*b;
|
|
panel_info *ppi;
|
|
#ifndef OLD_EXPOSE
|
|
int ex=10000,ey=10000,ex2=0,ey2=0;
|
|
#endif
|
|
|
|
while( !isTerminated )
|
|
{
|
|
if(My_XNextEvent(Dpy,&Event))
|
|
{
|
|
switch(Event.type)
|
|
{
|
|
case Expose:
|
|
PanelIndex = MainPanel;
|
|
while (PanelIndex && (PanelIndex->uber->IconWinParent != Event.xany.window))
|
|
PanelIndex = PanelIndex->next;
|
|
if (PanelIndex)
|
|
{ UberButton = PanelIndex->uber;
|
|
MyWindow = UberButton->IconWinParent;
|
|
}
|
|
else
|
|
break;
|
|
|
|
#ifdef OLD_EXPOSE
|
|
if(Event.xexpose.count == 0)
|
|
{
|
|
button=-1;ub=UberButton;
|
|
while(NextButton(&ub,&b,&button,1))
|
|
{
|
|
if(!ready && !(b->flags&b_Container))
|
|
MakeButton(b);
|
|
RedrawButton(b,1);
|
|
}
|
|
if(!ready)
|
|
ready++;
|
|
}
|
|
#else
|
|
ex=min(ex,Event.xexpose.x);
|
|
ey=min(ey,Event.xexpose.y);
|
|
ex2=max(ex2,Event.xexpose.x+Event.xexpose.width);
|
|
ey2=max(ey2,Event.xexpose.y+Event.xexpose.height);
|
|
|
|
if(Event.xexpose.count==0)
|
|
{
|
|
button=-1;ub=UberButton;
|
|
while(NextButton(&ub,&b,&button,1))
|
|
{
|
|
if(b->flags&b_Container)
|
|
{
|
|
x=buttonXPos(b,buttonNum(b));
|
|
y=buttonYPos(b,buttonNum(b));
|
|
}
|
|
else
|
|
{
|
|
x=buttonXPos(b,button);
|
|
y=buttonYPos(b,button);
|
|
}
|
|
if(!(ex > x + buttonWidth(b) || ex2 < x ||
|
|
ey > y + buttonHeight(b) || ey2 < y))
|
|
{
|
|
if(ready<1 && !(b->flags&b_Container))
|
|
MakeButton(b);
|
|
RedrawButton(b,1);
|
|
}
|
|
}
|
|
if(ready<1)
|
|
ready++;
|
|
|
|
ex=ey=10000;ex2=ey2=0;
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case ConfigureNotify:
|
|
/* XGetGeometry(Dpy, MyWindow, &root, &x, &y,
|
|
(ushort*)&tw,(ushort*)&th,
|
|
(ushort*)&border_width,(ushort*)&depth);
|
|
if(tw!=Width || th!=Height)
|
|
{
|
|
Width=tw;
|
|
Height=th;
|
|
SetButtonSize(UberButton,Width,Height);
|
|
button=-1;ub=UberButton;
|
|
while(NextButton(&ub,&b,&button,0))
|
|
MakeButton(b);
|
|
RedrawWindow(NULL);
|
|
} */ /* I don't like to change its size after it started */
|
|
break;
|
|
|
|
case KeyPress:
|
|
XLookupString(&Event.xkey,buffer,10,&keysym,0);
|
|
if(keysym!=XK_Return && keysym!=XK_KP_Enter && keysym!=XK_Linefeed)
|
|
break; /* fall through to ButtonPress */
|
|
case ButtonPress:
|
|
PanelIndex = MainPanel;
|
|
b = NULL;
|
|
do
|
|
if (PanelIndex->uber->swallow) /* is the panel shown? */
|
|
{
|
|
UberButton = PanelIndex->uber;
|
|
MyWindow = UberButton->IconWinParent;
|
|
if (Event.xany.window == MyWindow)
|
|
CurrentButton = b =
|
|
select_button(UberButton,Event.xbutton.x,Event.xbutton.y);
|
|
}
|
|
while (!b && PanelIndex->next && (PanelIndex = PanelIndex->next))
|
|
;
|
|
|
|
if(!b || !(b->flags&b_Action) ||
|
|
((act=GetButtonAction(b,Event.xbutton.button)) == NULL &&
|
|
(act=GetButtonAction(b,0)) == NULL))
|
|
{
|
|
CurrentButton=NULL;
|
|
break;
|
|
}
|
|
|
|
/* record the panel, the button pressed */
|
|
CurrentPanel = PanelIndex;
|
|
UberButton = CurrentPanel->uber;
|
|
MyWindow = UberButton->IconWinParent;
|
|
|
|
RedrawButton(b,0);
|
|
if(strncasecmp(act,"popup",5)!=0)
|
|
{
|
|
if (strncasecmp(act, "panel-", 6) == 0)
|
|
Slide(seekpanel(b), b);
|
|
break;
|
|
}
|
|
else /* i.e. action is Popup */
|
|
XUngrabPointer(Dpy,CurrentTime); /* And fall through */
|
|
|
|
case KeyRelease:
|
|
case ButtonRelease:
|
|
PanelIndex = MainPanel;
|
|
b = NULL;
|
|
do
|
|
{ if (PanelIndex->uber->swallow)
|
|
{
|
|
UberButton = PanelIndex->uber;
|
|
MyWindow = UberButton->IconWinParent;
|
|
if (Event.xany.window == MyWindow)
|
|
b=select_button(UberButton,Event.xbutton.x,Event.xbutton.y);
|
|
}
|
|
} while (!b && (PanelIndex = PanelIndex->next));
|
|
|
|
if(!(act=GetButtonAction(b,Event.xbutton.button)))
|
|
act=GetButtonAction(b,0);
|
|
if(b && b==CurrentButton && act)
|
|
{
|
|
if(strncasecmp(act,"Exec",4)==0)
|
|
{
|
|
/* close current subpanel */
|
|
if (PanelIndex != MainPanel)
|
|
Slide(PanelIndex, NULL);
|
|
|
|
/* Look for Exec "identifier", in which case the button
|
|
stays down until window "identifier" materializes */
|
|
i=4;
|
|
while(act[i]!=0 && act[i]!='"' &&
|
|
isspace(act[i]))
|
|
i++;
|
|
if(act[i] == '"')
|
|
{
|
|
i2=i+1;
|
|
while(act[i2]!=0 && act[i2]!='"')
|
|
i2++;
|
|
|
|
if(i2-i>1)
|
|
{
|
|
b->flags|=b_Hangon;
|
|
b->hangon = mymalloc(i2-i);
|
|
strncpy(b->hangon,&act[i+1],i2-i-1);
|
|
b->hangon[i2-i-1] = 0;
|
|
}
|
|
i2++;
|
|
}
|
|
else
|
|
i2=i;
|
|
|
|
tmp=mymalloc(strlen(act)+1);
|
|
strcpy(tmp,"Exec ");
|
|
while(act[i2]!=0 && isspace(act[i2]))
|
|
i2++;
|
|
strcat(tmp,&act[i2]);
|
|
MySendText(fd,tmp,0);
|
|
free(tmp);
|
|
}
|
|
else if(strncasecmp(act,"DumpButtons",11)==0)
|
|
DumpButtons(UberButton);
|
|
else if(strncasecmp(act,"SaveButtons",11)==0)
|
|
SaveButtons(UberButton);
|
|
else if(strncasecmp(act,"panel",5))
|
|
MySendText(fd,act,0);
|
|
}
|
|
|
|
/* recover the old record */
|
|
UberButton = CurrentPanel->uber; /* the panel, the button pressed */
|
|
MyWindow = UberButton->IconWinParent;
|
|
|
|
b=CurrentButton;
|
|
CurrentButton=NULL;
|
|
if(b)
|
|
RedrawButton(b,0);
|
|
break;
|
|
|
|
case ClientMessage:
|
|
if(Event.xclient.format==32 &&
|
|
Event.xclient.data.l[0]==_XA_WM_DEL_WIN)
|
|
{
|
|
for (ppi = MainPanel->next; ppi != NULL;
|
|
ppi = ppi->next)
|
|
{
|
|
if (ppi->uber->IconWinParent == Event.xany.window)
|
|
{
|
|
/* Only close the panel */
|
|
Slide(ppi, NULL);
|
|
break;
|
|
}
|
|
}
|
|
if (ppi == NULL)
|
|
DeadPipe(1);
|
|
}
|
|
break;
|
|
|
|
case PropertyNotify:
|
|
if(Event.xany.window==None)
|
|
break;
|
|
ub=UberButton;button=-1;
|
|
while(NextButton(&ub,&b,&button,0))
|
|
if((buttonSwallowCount(b)==3) && Event.xany.window==b->IconWin)
|
|
{
|
|
if(Event.xproperty.atom==XA_WM_NAME &&
|
|
buttonSwallow(b)&b_UseTitle)
|
|
{
|
|
if(b->flags&b_Title)
|
|
free(b->title);
|
|
b->flags|=b_Title;
|
|
XFetchName(Dpy,b->IconWin,&tmp);
|
|
CopyString(&b->title,tmp);
|
|
XFree(tmp);
|
|
MakeButton(b);
|
|
}
|
|
else if((Event.xproperty.atom==XA_WM_NORMAL_HINTS) &&
|
|
(!(buttonSwallow(b)&b_NoHints)))
|
|
{
|
|
long supp;
|
|
if(!XGetWMNormalHints(Dpy,b->IconWin,b->hints,&supp))
|
|
b->hints->flags = 0;
|
|
MakeButton(b);
|
|
}
|
|
RedrawButton(b,1);
|
|
}
|
|
break;
|
|
|
|
/* Not really sure if this is abandon all hope.. */
|
|
/* case UnmapNotify: */
|
|
case DestroyNotify:
|
|
ub=UberButton;button=-1;
|
|
while(NextButton(&ub,&b,&button,0))
|
|
if((buttonSwallowCount(b)==3) && Event.xany.window==b->IconWin)
|
|
{
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,
|
|
"%s: Button 0x%06x lost its window 0x%x (\"%s\")",
|
|
MyName,(ushort)b,(ushort)b->IconWin,b->hangon);
|
|
# endif
|
|
b->swallow&=~b_Count;
|
|
b->IconWin=None;
|
|
if(buttonSwallow(b)&b_Respawn && b->hangon && b->spawn)
|
|
{
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,", respawning\n");
|
|
# endif
|
|
b->swallow|=1;
|
|
b->flags|=b_Swallow|b_Hangon;
|
|
MySendText(fd,b->spawn,0);
|
|
}
|
|
else
|
|
{
|
|
b->flags&=~b_Swallow;
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"\n");
|
|
# endif
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
# ifdef DEBUG_EVENTS
|
|
fprintf(stderr,"%s: Event fell through unhandled\n",MyName);
|
|
# endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*** RedrawWindow()
|
|
*** Draws the window by traversing the button tree, draws all if NULL is given,
|
|
*** otherwise only the given button.
|
|
**/
|
|
void RedrawWindow(button_info *b)
|
|
{
|
|
int button;
|
|
XEvent dummy;
|
|
button_info *ub;
|
|
|
|
if(ready<1)
|
|
return;
|
|
|
|
/* Flush expose events */
|
|
while (XCheckTypedWindowEvent (Dpy, MyWindow, Expose, &dummy));
|
|
|
|
if(b)
|
|
{
|
|
RedrawButton(b,0);
|
|
return;
|
|
}
|
|
|
|
button=-1;ub=UberButton;
|
|
while(NextButton(&ub,&b,&button,1))
|
|
RedrawButton(b,1);
|
|
}
|
|
|
|
/**
|
|
*** LoadIconFile()
|
|
**/
|
|
int LoadIconFile(char *s,FvwmPicture **p)
|
|
{
|
|
*p=CachePicture(Dpy,Root,iconPath,pixmapPath,s, save_color_limit);
|
|
if(*p)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*** RecursiveLoadData()
|
|
*** Loads colors, fonts and icons, and calculates buttonsizes
|
|
**/
|
|
void RecursiveLoadData(button_info *b,int *maxx,int *maxy)
|
|
{
|
|
int i,j,x=0,y=0;
|
|
XFontStruct *font;
|
|
|
|
if(!b) return;
|
|
|
|
#ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,"%s: Loading: Button 0x%06x: colors",MyName,(ushort)b);
|
|
#endif
|
|
|
|
/* Load colors */
|
|
if(b->flags&b_Fore)
|
|
b->fc=GetColor(b->fore);
|
|
if(b->flags&b_Back)
|
|
{
|
|
if(b->flags&b_IconBack)
|
|
{
|
|
if(!LoadIconFile(b->back,&b->backicon))
|
|
b->flags&=~b_Back;
|
|
}
|
|
else
|
|
{
|
|
b->bc=GetColor(b->back);
|
|
b->hc=GetHilite(b->bc);
|
|
b->sc=GetShadow(b->bc);
|
|
}
|
|
}
|
|
if(b->flags&b_Container)
|
|
{
|
|
# ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,", colors2");
|
|
# endif
|
|
if(b->c->flags&b_Fore)
|
|
b->c->fc=GetColor(b->c->fore);
|
|
if(b->c->flags&b_Back)
|
|
{
|
|
if(b->c->flags&b_IconBack && !(b->c->flags&b_TransBack))
|
|
{
|
|
if(!LoadIconFile(b->c->back_file,&b->c->backicon))
|
|
b->c->flags&=~b_IconBack;
|
|
}
|
|
|
|
{
|
|
b->c->bc=GetColor(b->c->back);
|
|
b->c->hc=GetHilite(b->c->bc);
|
|
b->c->sc=GetShadow(b->c->bc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Load the font */
|
|
if(b->flags&b_Font)
|
|
{
|
|
# ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,", font \"%s\"",b->font_string);
|
|
# endif
|
|
|
|
fprintf(stderr, "b=0x%lx, font_string=%s\n",
|
|
(unsigned long)b, b? b->font_string : "(NULL)");
|
|
if(strncasecmp(b->font_string,"none",4)==0)
|
|
b->font=NULL;
|
|
else if(!(b->font=XLoadQueryFont(Dpy,b->font_string)))
|
|
{
|
|
b->flags&=~b_Font;
|
|
fprintf(stderr,"%s: Couldn't load font %s\n",MyName,
|
|
b->font_string);
|
|
}
|
|
}
|
|
|
|
if(b->flags&b_Container && b->c->flags&b_Font)
|
|
{
|
|
# ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,", font2 \"%s\"",b->c->font_string);
|
|
# endif
|
|
if(strncasecmp(b->c->font_string,"none",4)==0)
|
|
b->c->font=NULL;
|
|
else if(!(b->c->font=XLoadQueryFont(Dpy,b->c->font_string)))
|
|
{
|
|
fprintf(stderr,"%s: Couldn't load font %s\n",MyName,
|
|
b->c->font_string);
|
|
if(b==UberButton)
|
|
{
|
|
if(!(b->c->font=XLoadQueryFont(Dpy,"fixed")))
|
|
fprintf(stderr,"%s: Couldn't load font fixed\n",MyName);
|
|
}
|
|
else
|
|
b->c->flags&=~b_Font;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Calculate subbutton sizes */
|
|
if(b->flags&b_Container && b->c->num_buttons)
|
|
{
|
|
# ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,", entering container\n");
|
|
# endif
|
|
for(i=0;i<b->c->num_buttons;i++)
|
|
if(b->c->buttons[i])
|
|
RecursiveLoadData(b->c->buttons[i],&x,&y);
|
|
|
|
if(b->c->flags&b_Size)
|
|
{
|
|
x=b->c->minx;
|
|
y=b->c->miny;
|
|
}
|
|
# ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,"%s: Loading: Back to container 0x%06x",MyName,(ushort)b);
|
|
# endif
|
|
|
|
b->c->ButtonWidth=x;
|
|
b->c->ButtonHeight=y;
|
|
x*=b->c->num_columns;
|
|
y*=b->c->num_rows;
|
|
}
|
|
|
|
|
|
|
|
i=0;j=0;
|
|
|
|
/* Load the icon */
|
|
if(b->flags&b_Icon && LoadIconFile(b->icon_file,&b->icon))
|
|
{
|
|
# ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,", icon \"%s\"",b->icon_file);
|
|
# endif
|
|
i=b->icon->width;
|
|
j=b->icon->height;
|
|
}
|
|
else
|
|
b->flags&=~b_Icon;
|
|
|
|
if(b->flags&b_Title && (font=buttonFont(b)))
|
|
{
|
|
# ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,", title \"%s\"",b->title);
|
|
# endif
|
|
if(buttonJustify(b)&b_Horizontal)
|
|
{
|
|
i+=buttonXPad(b)+XTextWidth(font,b->title,strlen(b->title));
|
|
j=max(j,font->ascent+font->descent);
|
|
}
|
|
else
|
|
{
|
|
i=max(i,XTextWidth(font,b->title,strlen(b->title)));
|
|
j+=font->ascent+font->descent;
|
|
}
|
|
}
|
|
|
|
x+=i;
|
|
y+=j;
|
|
|
|
if(b->flags&b_Size)
|
|
{
|
|
x=b->minx;
|
|
y=b->miny;
|
|
}
|
|
|
|
x+=2*(buttonFrame(b)+buttonXPad(b));
|
|
y+=2*(buttonFrame(b)+buttonYPad(b));
|
|
|
|
x/=b->BWidth;
|
|
y/=b->BHeight;
|
|
|
|
*maxx=max(x,*maxx);
|
|
*maxy=max(y,*maxy);
|
|
# ifdef DEBUG_LOADDATA
|
|
fprintf(stderr,", size %ux%u, done\n",x,y);
|
|
# endif
|
|
}
|
|
|
|
/**
|
|
*** CreateWindow()
|
|
*** Sizes and creates the window
|
|
**/
|
|
void CreateWindow(button_info *ub,int maxx,int maxy)
|
|
{
|
|
XSizeHints mysizehints;
|
|
XGCValues gcv;
|
|
unsigned long gcm;
|
|
XClassHint myclasshints;
|
|
|
|
x = CurrentPanel->uber->x; /* Geometry x where to put the panel */
|
|
y = CurrentPanel->uber->y; /* Geometry y where to put the panel */
|
|
xneg = CurrentPanel->uber->w;
|
|
yneg = CurrentPanel->uber->h;
|
|
|
|
if(maxx<16)
|
|
maxx=16;
|
|
if(maxy<16)
|
|
maxy=16;
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"making atoms...");
|
|
# endif
|
|
|
|
_XA_WM_DEL_WIN = XInternAtom(Dpy,"WM_DELETE_WINDOW",0);
|
|
_XA_WM_PROTOCOLS = XInternAtom (Dpy, "WM_PROTOCOLS",0);
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"sizing...");
|
|
# endif
|
|
|
|
mysizehints.flags = PWinGravity | PResizeInc | PBaseSize;
|
|
|
|
mysizehints.base_width=mysizehints.base_height=0;
|
|
|
|
/* This should never be executed anyway, let's remove it.
|
|
if(ub->flags&b_Frame)
|
|
{
|
|
mysizehints.base_width+=2*abs(ub->framew);
|
|
mysizehints.base_height+=2*abs(ub->framew);
|
|
}
|
|
if(ub->flags&b_Padding)
|
|
{
|
|
mysizehints.base_width+=2*ub->xpad;
|
|
mysizehints.base_height+=2*ub->ypad;
|
|
}
|
|
*/
|
|
mysizehints.width=mysizehints.base_width+maxx;
|
|
mysizehints.height=mysizehints.base_height+maxy;
|
|
mysizehints.width_inc=ub->c->num_columns;
|
|
mysizehints.height_inc=ub->c->num_rows;
|
|
mysizehints.base_height+=ub->c->num_rows*2;
|
|
mysizehints.base_width+=ub->c->num_columns*2;
|
|
|
|
if(w>-1) /* from geometry */
|
|
{
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"constraining (w=%i)...",w);
|
|
# endif
|
|
ConstrainSize(&mysizehints,&w,&h);
|
|
mysizehints.width = w;
|
|
mysizehints.height = h;
|
|
mysizehints.flags |= USSize;
|
|
}
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"gravity...");
|
|
# endif
|
|
mysizehints.x=0;
|
|
mysizehints.y=0;
|
|
if(x > -30000)
|
|
{
|
|
if (xneg)
|
|
{
|
|
mysizehints.x = DisplayWidth(Dpy,screen) + x - mysizehints.width;
|
|
gravity = NorthEastGravity;
|
|
}
|
|
else
|
|
mysizehints.x = x;
|
|
if (yneg)
|
|
{
|
|
mysizehints.y = DisplayHeight(Dpy,screen) + y - mysizehints.height;
|
|
gravity = SouthWestGravity;
|
|
}
|
|
else
|
|
mysizehints.y = y;
|
|
if(xneg && yneg)
|
|
gravity = SouthEastGravity;
|
|
mysizehints.flags |= USPosition;
|
|
}
|
|
mysizehints.win_gravity = gravity;
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"colors...");
|
|
# endif
|
|
if(d_depth < 2)
|
|
{
|
|
back_pix = GetColor("white");
|
|
fore_pix = GetColor("black");
|
|
hilite_pix = back_pix;
|
|
shadow_pix = fore_pix;
|
|
}
|
|
else
|
|
{
|
|
back_pix = GetColor(ub->c->back);
|
|
fore_pix = GetColor(ub->c->fore);
|
|
hilite_pix = GetHilite(back_pix);
|
|
shadow_pix = GetShadow(back_pix);
|
|
}
|
|
|
|
# ifdef DEBUG_INIT
|
|
if(mysizehints.flags&USPosition)
|
|
fprintf(stderr,"create(%i,%i,%u,%u,1,%u,%u)...",
|
|
mysizehints.x,mysizehints.y,
|
|
mysizehints.width,mysizehints.height,
|
|
(ushort)fore_pix,(ushort)back_pix);
|
|
else
|
|
fprintf(stderr,"create(-,-,%u,%u,1,%u,%u)...",
|
|
mysizehints.width,mysizehints.height,
|
|
(ushort)fore_pix,(ushort)back_pix);
|
|
# endif
|
|
|
|
MyWindow = XCreateSimpleWindow(Dpy,Root,mysizehints.x,mysizehints.y,
|
|
mysizehints.width,mysizehints.height,
|
|
0,fore_pix,back_pix);
|
|
if(ub->c->flags&b_IconBack && !(ub->c->flags&b_TransBack))
|
|
XSetWindowBackgroundPixmap(Dpy,MyWindow,ub->c->backicon->picture);
|
|
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"properties...");
|
|
# endif
|
|
XSetWMProtocols(Dpy,MyWindow,&_XA_WM_DEL_WIN,1);
|
|
|
|
#if 0
|
|
myclasshints.res_name=strdup((CurrentPanel == MainPanel)
|
|
? MyName : CurrentPanel->uber->title);
|
|
#else
|
|
if (CurrentPanel == MainPanel)
|
|
{
|
|
myclasshints.res_name=strdup(MyName);
|
|
}
|
|
else
|
|
{
|
|
myclasshints.res_name=(char *)malloc(strlen(MyName)+6);
|
|
strcpy(myclasshints.res_name,MyName);
|
|
strcat(myclasshints.res_name,"Panel");
|
|
}
|
|
#endif
|
|
myclasshints.res_class=strdup((CurrentPanel == MainPanel)
|
|
? "FvwmButtons" : "FvwmButtonsPanel");
|
|
|
|
{
|
|
XTextProperty mynametext;
|
|
char *list[]={NULL,NULL};
|
|
list[0]=(CurrentPanel == MainPanel) ? MyName : CurrentPanel->uber->title;
|
|
if(!XStringListToTextProperty(list,1,&mynametext))
|
|
{
|
|
fprintf(stderr,"%s: Failed to convert name to XText\n",MyName);
|
|
exit(1);
|
|
}
|
|
XSetWMProperties(Dpy,MyWindow,&mynametext,&mynametext,
|
|
NULL,0,&mysizehints,NULL,&myclasshints);
|
|
XFree(mynametext.value);
|
|
}
|
|
|
|
XSelectInput(Dpy,MyWindow,MW_EVENTS);
|
|
|
|
# ifdef DEBUG_INIT
|
|
fprintf(stderr,"GC...");
|
|
# endif
|
|
gcm = GCForeground|GCBackground;
|
|
gcv.foreground = fore_pix;
|
|
gcv.background = back_pix;
|
|
if(ub && ub->c && ub->c->font && ub->font)
|
|
{
|
|
gcv.font = ub->c->font->fid;
|
|
gcm |= GCFont;
|
|
}
|
|
NormalGC = XCreateGC(Dpy, Root, gcm, &gcv);
|
|
|
|
free(myclasshints.res_class);
|
|
free(myclasshints.res_name);
|
|
}
|
|
|
|
/* ----------------------------- color functions --------------------------- */
|
|
|
|
#ifdef XPM
|
|
# define MyAllocColor(a,b,c) PleaseAllocColor(c)
|
|
#else
|
|
# define MyAllocColor(a,b,c) XAllocColor(a,b,c)
|
|
#endif
|
|
|
|
/**
|
|
*** PleaseAllocColor()
|
|
*** Allocs a color through XPM, which does it's closeness thing if out of
|
|
*** space.
|
|
**/
|
|
#ifdef XPM
|
|
int PleaseAllocColor(XColor *color)
|
|
{
|
|
char *xpm[] = {"1 1 1 1",NULL,"x"};
|
|
XpmAttributes attr;
|
|
XImage *dummy1=None,*dummy2=None;
|
|
static char buf[20];
|
|
|
|
sprintf(buf,"x c #%04x%04x%04x",color->red,color->green,color->blue);
|
|
xpm[1]=buf;
|
|
attr.valuemask=XpmCloseness;
|
|
attr.closeness=40000; /* value used by fvwm and fvwmlib */
|
|
|
|
if(XpmCreateImageFromData(Dpy,xpm,&dummy1,&dummy2,&attr)!=XpmSuccess)
|
|
{
|
|
fprintf(stderr,"%s: Unable to get similar color\n",MyName);
|
|
exit(1);
|
|
}
|
|
color->pixel=XGetPixel(dummy1,0,0);
|
|
if(dummy1!=None)XDestroyImage(dummy1);
|
|
if(dummy2!=None)XDestroyImage(dummy2);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
*** nocolor()
|
|
*** Complain
|
|
**/
|
|
void nocolor(const char *a, const char *b)
|
|
{
|
|
fprintf(stderr,"%s: Can't %s %s, quitting, sorry...\n", MyName, a,b);
|
|
exit(1);
|
|
}
|
|
|
|
/**
|
|
*** GetColor()
|
|
*** Loads a single color
|
|
**/
|
|
Pixel GetColor(char *name)
|
|
{
|
|
XColor color;
|
|
XWindowAttributes attributes;
|
|
|
|
XGetWindowAttributes(Dpy,Root,&attributes);
|
|
color.pixel = 0;
|
|
if (!XParseColor (Dpy, attributes.colormap, name, &color))
|
|
nocolor("parse",name);
|
|
else if(!MyAllocColor(Dpy,attributes.colormap,&color))
|
|
nocolor("alloc",name);
|
|
return color.pixel;
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------------*/
|
|
|
|
#ifdef DEBUG_EVENTS
|
|
void DebugEvents(XEvent *event)
|
|
{
|
|
char *event_names[]={NULL,NULL,
|
|
"KeyPress","KeyRelease","ButtonPress",
|
|
"ButtonRelease","MotionNotify","EnterNotify",
|
|
"LeaveNotify","FocusIn","FocusOut",
|
|
"KeymapNotify","Expose","GraphicsExpose",
|
|
"NoExpose","VisibilityNotify","CreateNotify",
|
|
"DestroyNotify","UnmapNotify","MapNotify",
|
|
"MapRequest","ReparentNotify","ConfigureNotify",
|
|
"ConfigureRequest","GravityNotify","ResizeRequest",
|
|
"CirculateNotify","CirculateRequest","PropertyNotify",
|
|
"SelectionClear","SelectionRequest","SelectionNotify",
|
|
"ColormapNotify","ClientMessage","MappingNotify"};
|
|
fprintf(stderr,"%s: Received %s event from window 0x%x\n",
|
|
MyName,event_names[event->type],(ushort)event->xany.window);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG_FVWM
|
|
void DebugFvwmEvents(unsigned long type)
|
|
{
|
|
char *events[]={
|
|
"M_NEW_PAGE",
|
|
"M_NEW_DESK",
|
|
"M_ADD_WINDOW",
|
|
"M_RAISE_WINDOW",
|
|
"M_LOWER_WINDOW",
|
|
"M_CONFIGURE_WINDOW",
|
|
"M_FOCUS_CHANGE",
|
|
"M_DESTROY_WINDOW",
|
|
"M_ICONIFY",
|
|
"M_DEICONIFY",
|
|
"M_WINDOW_NAME",
|
|
"M_ICON_NAME",
|
|
"M_RES_CLASS",
|
|
"M_RES_NAME",
|
|
"M_END_WINDOWLIST",
|
|
"M_ICON_LOCATION",
|
|
"M_MAP",
|
|
"M_ERROR",
|
|
"M_CONFIG_INFO",
|
|
"M_END_CONFIG_INFO",
|
|
"M_ICON_FILE",
|
|
"M_DEFAULTICON",NULL};
|
|
int i=0;
|
|
while(events[i])
|
|
{
|
|
if(type&1<<i)
|
|
fprintf(stderr,"%s: Received %s message from fvwm\n",MyName,events[i]);
|
|
i++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
*** My_XNextEvent()
|
|
*** Waits for next X event, or for an auto-raise timeout.
|
|
**/
|
|
int My_XNextEvent(Display *Dpy, XEvent *event)
|
|
{
|
|
fd_set in_fdset;
|
|
unsigned long header[HEADER_SIZE];
|
|
int count;
|
|
static int miss_counter = 0;
|
|
unsigned long *body;
|
|
|
|
if(XPending(Dpy))
|
|
{
|
|
XNextEvent(Dpy,event);
|
|
# ifdef DEBUG_EVENTS
|
|
DebugEvents(event);
|
|
# endif
|
|
return 1;
|
|
}
|
|
|
|
FD_ZERO(&in_fdset);
|
|
FD_SET(x_fd,&in_fdset);
|
|
FD_SET(fd[1],&in_fdset);
|
|
|
|
if (select(fd_width,SELECT_TYPE_ARG234 &in_fdset, 0, 0, NULL) > 0)
|
|
{
|
|
|
|
if(FD_ISSET(x_fd, &in_fdset))
|
|
{
|
|
if(XPending(Dpy))
|
|
{
|
|
XNextEvent(Dpy,event);
|
|
miss_counter = 0;
|
|
# ifdef DEBUG_EVENTS
|
|
DebugEvents(event);
|
|
# endif
|
|
return 1;
|
|
}
|
|
else
|
|
miss_counter++;
|
|
if(miss_counter > 100)
|
|
DeadPipe(0);
|
|
}
|
|
|
|
if(FD_ISSET(fd[1], &in_fdset))
|
|
{
|
|
if((count = ReadFvwmPacket(fd[1], header, &body)) > 0)
|
|
{
|
|
process_message(header[1],body);
|
|
free(body);
|
|
}
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*** SpawnSome()
|
|
*** Is run after the windowlist is checked. If any button hangs on UseOld,
|
|
*** it has failed, so we try to spawn a window for them.
|
|
**/
|
|
void SpawnSome()
|
|
{
|
|
static char first=1;
|
|
button_info *b,*ub=UberButton;
|
|
int button=-1;
|
|
if(!first)
|
|
return;
|
|
first=0;
|
|
while(NextButton(&ub,&b,&button,0))
|
|
if((buttonSwallowCount(b)==1) && b->flags&b_Hangon &&
|
|
buttonSwallow(b)&b_UseOld)
|
|
if(b->spawn)
|
|
{
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"%s: Button 0x%06x did not find a \"%s\" window, %s",
|
|
MyName,(ushort)b,b->hangon,"spawning own\n");
|
|
# endif
|
|
SendText(fd,b->spawn,0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*** process_message()
|
|
*** Process window list messages
|
|
**/
|
|
void process_message(unsigned long type,unsigned long *body)
|
|
{
|
|
# ifdef DEBUG_FVWM
|
|
DebugFvwmEvents(type);
|
|
# endif
|
|
panel_info *PanelIndex = MainPanel;
|
|
do
|
|
{
|
|
UberButton = PanelIndex->uber;
|
|
MyWindow = UberButton->IconWinParent;
|
|
|
|
switch(type)
|
|
{
|
|
case M_NEW_DESK:
|
|
new_desk = body[0];
|
|
RedrawWindow(NULL);
|
|
break;
|
|
case M_END_WINDOWLIST:
|
|
SpawnSome();
|
|
RedrawWindow(NULL);
|
|
break;
|
|
case M_MAP:
|
|
swallow(body);
|
|
break;
|
|
case M_RES_NAME:
|
|
case M_RES_CLASS:
|
|
case M_WINDOW_NAME:
|
|
CheckForHangon(body);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
} while ((PanelIndex = PanelIndex->next));
|
|
}
|
|
|
|
|
|
/* --------------------------------- swallow code -------------------------- */
|
|
|
|
/**
|
|
*** CheckForHangon()
|
|
*** Is the window here now?
|
|
**/
|
|
void CheckForHangon(unsigned long *body)
|
|
{
|
|
button_info *b,*ub=UberButton;
|
|
int button=-1;
|
|
ushort d;
|
|
char *cbody;
|
|
cbody = (char *)&body[3];
|
|
|
|
while(NextButton(&ub,&b,&button,0))
|
|
if(b->flags&b_Hangon && strcmp(cbody,b->hangon)==0)
|
|
{
|
|
/* Is this a swallowing button in state 1? */
|
|
if(buttonSwallowCount(b)==1)
|
|
{
|
|
b->swallow&=~b_Count;
|
|
b->swallow|=2;
|
|
b->IconWin=(Window)body[0];
|
|
b->flags&=~b_Hangon;
|
|
|
|
/* We get the parent of the window to compare with later... */
|
|
b->IconWinParent=
|
|
GetRealGeometry(Dpy,b->IconWin,
|
|
&b->x,&b->y,&b->w,&b->h,&b->bw,&d);
|
|
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"%s: Button 0x%06x %s 0x%lx \"%s\", parent 0x%lx\n",
|
|
MyName,(ushort)b,"will swallow window",body[0],cbody,
|
|
b->IconWinParent);
|
|
# endif
|
|
|
|
if(buttonSwallow(b)&b_UseOld)
|
|
swallow(body);
|
|
}
|
|
else
|
|
{
|
|
/* Else it is an executing button with a confirmed kill */
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"%s: Button 0x%06x %s 0x%lx \"%s\", released\n",
|
|
MyName,(int)b,"hung on window",body[0],cbody);
|
|
# endif
|
|
b->flags&=~b_Hangon;
|
|
free(b->hangon);
|
|
b->hangon=NULL;
|
|
RedrawButton(b,0);
|
|
|
|
}
|
|
break;
|
|
}
|
|
else if(buttonSwallowCount(b)>=2 && (Window)body[0]==b->IconWin)
|
|
break; /* This window has already been swallowed by someone else! */
|
|
}
|
|
|
|
/**
|
|
*** GetRealGeometry()
|
|
*** Traverses window tree to find the real x,y of a window. Any simpler?
|
|
*** Returns parent window, or None if failed.
|
|
**/
|
|
Window GetRealGeometry(Display *dpy,Window win,int *x,int *y,ushort *w,
|
|
ushort *h,ushort *bw,ushort *d)
|
|
{
|
|
Window root;
|
|
Window rp=None;
|
|
Window *children;
|
|
unsigned int n;
|
|
|
|
if(!XGetGeometry(dpy,win,&root,x,y,w,h,bw,d))
|
|
return None;
|
|
|
|
/* Now, x and y are not correct. They are parent relative, not root... */
|
|
/* Hmm, ever heard of XTranslateCoordinates!? */
|
|
|
|
XTranslateCoordinates(dpy,win,root,*x,*y,x,y,&rp);
|
|
|
|
XQueryTree(dpy,win,&root,&rp,&children,&n);
|
|
if (children)
|
|
XFree(children);
|
|
|
|
return rp;
|
|
}
|
|
|
|
/**
|
|
*** swallow()
|
|
*** Executed when swallowed windows get mapped
|
|
**/
|
|
void swallow(unsigned long *body)
|
|
{
|
|
char *temp;
|
|
button_info *ub=UberButton,*b;
|
|
int button=-1;
|
|
ushort d;
|
|
Window p;
|
|
|
|
while(NextButton(&ub,&b,&button,0))
|
|
if((b->IconWin==(Window)body[0]) && (buttonSwallowCount(b)==2))
|
|
{
|
|
/* Store the geometry in case we need to unswallow. Get parent */
|
|
p=GetRealGeometry(Dpy,b->IconWin,&b->x,&b->y,&b->w,&b->h,&b->bw,&d);
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"%s: Button 0x%06x %s 0x%lx, with parent 0x%lx\n",
|
|
MyName,(ushort)b,"trying to swallow window",body[0],p);
|
|
# endif
|
|
|
|
if(p==None) /* This means the window is no more */ /* NO! wrong */
|
|
{
|
|
fprintf(stderr,"%s: Window 0x%lx (\"%s\") disappeared %s\n",
|
|
MyName,b->IconWin,b->hangon,"before swallow complete");
|
|
/* Now what? Nothing? For now: give up that button */
|
|
b->flags&=~(b_Hangon|b_Swallow);
|
|
return;
|
|
}
|
|
|
|
if(p!=b->IconWinParent) /* The window has been reparented */
|
|
{
|
|
fprintf(stderr,"%s: Window 0x%lx (\"%s\") was %s (window 0x%lx)\n",
|
|
MyName,b->IconWin,b->hangon,"grabbed by someone else",p);
|
|
|
|
/* Request a new windowlist, we might have ignored another
|
|
matching window.. */
|
|
SendText(fd,"Send_WindowList",0);
|
|
|
|
/* Back one square and lose one turn */
|
|
b->swallow&=~b_Count;
|
|
b->swallow|=1;
|
|
b->flags|=b_Hangon;
|
|
return;
|
|
}
|
|
|
|
# ifdef DEBUG_HANGON
|
|
fprintf(stderr,"%s: Button 0x%06x swallowed window 0x%lx\n",
|
|
MyName,(ushort)b,body[0]);
|
|
# endif
|
|
|
|
b->swallow&=~b_Count;
|
|
b->swallow|=3;
|
|
|
|
/* "Swallow" the window! Place it in the void so we don't see it
|
|
until it's MoveResize'd */
|
|
XReparentWindow(Dpy,b->IconWin,MyWindow,-1500,-1500);
|
|
XSelectInput(Dpy,b->IconWin,SW_EVENTS);
|
|
if(buttonSwallow(b)&b_UseTitle)
|
|
{
|
|
if(b->flags&b_Title)
|
|
free(b->title);
|
|
b->flags|=b_Title;
|
|
XFetchName(Dpy,b->IconWin,&temp);
|
|
CopyString(&b->title,temp);
|
|
XFree(temp);
|
|
}
|
|
XMapWindow(Dpy,b->IconWin);
|
|
MakeButton(b);
|
|
RedrawButton(b,1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Button
|
|
* b->flags |= b_Action (though b->hangon is ilegally used)
|
|
* b->action[0] = "panel-u", "panel-l", "panel-d", or "panel-r"
|
|
* b->hangon = panel title (case sensitive)
|
|
* Panel
|
|
* uber->flags |= b_Container (though the following fields are ilegally
|
|
* used)
|
|
* uber->title = the title of the panel
|
|
* uber->IconWinParent = the panel window, will be assigned to MyWindow
|
|
* uber->swallow = 0:hidden 1:shown
|
|
* uber->icon_w = panel width
|
|
* uber->icon_h = panel height
|
|
* uber->x = x position to start the panel wrt the button b
|
|
* uber->y = y position to start the panel wrt the button b
|
|
* uber->w = xneg
|
|
* uber->h = yneg
|
|
* uber->n = cast to 'char' to record of the direction
|
|
*
|
|
* CurrentPanel = the panel where the button b was pressed to popup a new
|
|
* panel
|
|
*/
|
|
|
|
#define PanelPopUpStep 32
|
|
|
|
panel_info *seekpanel (button_info *b)
|
|
{
|
|
panel_info *PanelIndex = MainPanel->next; /* skip the main panel */
|
|
|
|
while (PanelIndex && strcmp(b->hangon, PanelIndex->uber->title))
|
|
PanelIndex = PanelIndex->next;
|
|
|
|
return PanelIndex;
|
|
}
|
|
|
|
void Slide (panel_info *p, button_info *b)
|
|
{
|
|
Window PanelWin;
|
|
Window root;
|
|
int x, y, iw, ih, BW, depth;
|
|
char direction;
|
|
ushort i, c, xstep, ystep, wstep, hstep;
|
|
|
|
if (!p)
|
|
/* no such panel */
|
|
return;
|
|
/* PanelWin is found */
|
|
PanelWin = p->uber->IconWinParent;
|
|
|
|
direction = b ? b->action[0][6] : (char) p->uber->n;
|
|
|
|
if (p->uber->swallow)
|
|
{
|
|
/* shown ---> hidden */
|
|
root = GetRealGeometry(Dpy, PanelWin, &x, &y,
|
|
(ushort*)&iw, (ushort*)&ih,
|
|
(ushort*)&BW, (ushort*)&depth);
|
|
|
|
switch (direction)
|
|
{
|
|
case 'l':
|
|
c = iw / PanelPopUpStep;
|
|
xstep = wstep = PanelPopUpStep;
|
|
ystep = hstep = 0;
|
|
break;
|
|
case 'r':
|
|
c = iw / PanelPopUpStep;
|
|
wstep = PanelPopUpStep;
|
|
xstep = ystep = hstep = 0;
|
|
break;
|
|
case 'd':
|
|
c = ih / PanelPopUpStep;
|
|
hstep = PanelPopUpStep;
|
|
xstep = ystep = wstep = 0;
|
|
break;
|
|
case 'g':
|
|
/* just pop down without animation */
|
|
c = 0;
|
|
break;
|
|
case 'u':
|
|
default:
|
|
c = ih / PanelPopUpStep;
|
|
ystep = hstep = PanelPopUpStep;
|
|
xstep = wstep = 0;
|
|
break;
|
|
}
|
|
|
|
for (i = 1; i < c; i++)
|
|
{
|
|
iw -= wstep;
|
|
ih -= hstep;
|
|
x += xstep;
|
|
y += ystep;
|
|
XMoveResizeWindow(Dpy, PanelWin, x, y, iw, ih);
|
|
}
|
|
|
|
XUnmapWindow(Dpy, PanelWin);
|
|
p->uber->swallow = 0;
|
|
}
|
|
else
|
|
{
|
|
/* hidden ---> shown */
|
|
int ix = buttonXPos(b, b->n); /* button in the CurrentPanel */
|
|
int iy = buttonYPos(b, b->n); /* button in the CurrentPanel */
|
|
|
|
int mw = p->uber->icon_w; /* panel menu width */
|
|
int mh = p->uber->icon_h; /* panel menu height */
|
|
int w; /* current width */
|
|
int h; /* current height */
|
|
|
|
root = GetRealGeometry(Dpy, CurrentPanel->uber->IconWinParent, &x, &y,
|
|
(ushort*)&iw, (ushort*)&ih,
|
|
(ushort*)&BW, (ushort*)&depth);
|
|
|
|
x += p->uber->x + ix;
|
|
y += p->uber->y + iy;
|
|
c = 0;
|
|
|
|
/* initial position and size */
|
|
switch (direction)
|
|
{
|
|
case 'g':
|
|
/* just pop up without animation */
|
|
c = 0;
|
|
break;
|
|
case 'l':
|
|
case 'r':
|
|
h = mh;
|
|
w = mw % PanelPopUpStep;
|
|
if (w == 0)
|
|
{
|
|
w = PanelPopUpStep;
|
|
c--;
|
|
}
|
|
if (direction == 'l')
|
|
{
|
|
x -= w;
|
|
c += mw / PanelPopUpStep;
|
|
xstep = wstep = PanelPopUpStep;
|
|
ystep = hstep = 0;
|
|
}
|
|
else
|
|
{
|
|
x += b->BWidth * b->parent->c->ButtonWidth;
|
|
c += mw / PanelPopUpStep;
|
|
wstep = PanelPopUpStep;
|
|
xstep = ystep = hstep = 0;
|
|
}
|
|
break;
|
|
case 'd':
|
|
case 'u':
|
|
default:
|
|
w = mw;
|
|
h = mh % PanelPopUpStep;
|
|
if (h == 0)
|
|
{
|
|
h = PanelPopUpStep;
|
|
c--;
|
|
}
|
|
if (direction == 'd')
|
|
{
|
|
y += b->BHeight * b->parent->c->ButtonHeight;
|
|
c += mh / PanelPopUpStep;
|
|
hstep = PanelPopUpStep;
|
|
xstep = ystep = wstep = 0;
|
|
}
|
|
else
|
|
{
|
|
y -= h;
|
|
c += mh / PanelPopUpStep;
|
|
ystep = hstep = PanelPopUpStep;
|
|
xstep = wstep = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (c > 0)
|
|
XMoveResizeWindow(Dpy, PanelWin, x, y, w, h);
|
|
XMapSubwindows(Dpy, PanelWin);
|
|
XMapWindow(Dpy, PanelWin);
|
|
|
|
for (i = 0; i < c; i++)
|
|
{
|
|
x -= xstep;
|
|
w += wstep;
|
|
y -= ystep;
|
|
h += hstep;
|
|
XMoveResizeWindow(Dpy, PanelWin, x, y, w, h);
|
|
}
|
|
|
|
p->uber->n = (int) direction;
|
|
p->uber->swallow = 1;
|
|
}
|
|
}
|