/* FvwmWinList Module for Fvwm. * * Copyright 1994, Mike Finger (mfinger@mermaid.micro.umn.edu or * Mike_Finger@atk.com) * * The author makes not guarantees or warantees, either express or * implied. Feel free to use any contained here for any purpose, as long * and this and any other applicible copyrights are kept intact. * The functions in this source file that are based on part of the FvwmIdent * module for Fvwm are noted by a small copyright atop that function, all others * are copyrighted by Mike Finger. For those functions modified/used, here is * the full, origonal copyright: * * Copyright 1994, Robert Nation and Nobutaka Suzuki. * 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. */ #define TRUE 1 #define FALSE #define NO_CONSOLE #ifndef min #define min(a,b) (((a)<(b)) ? (a) : (b)) #define max(a,b) (((a)>(b)) ? (a) : (b)) #endif #define YES "Yes" #define NO "No" #include "../../configure.h" #include #include #include #include #include #include #include #if defined ___AIX || defined _AIX || defined __QNX__ || defined ___AIXV3 || defined AIXV3 || defined _SEQUENT_ #include #endif #include #include #ifdef ISC /* Saul */ #include /* Saul */ #endif /* Saul */ #include #include #include #include #include #include #include #include "../../fvwm/module.h" #include "../../version.h" #include "FvwmWinList.h" #include "ButtonArray.h" #include "List.h" #include "Colors.h" #include "Mallocs.h" #define GRAB_EVENTS (ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|EnterWindowMask|LeaveWindowMask) #define SomeButtonDown(a) ((a)&Button1Mask||(a)&Button2Mask||(a)&Button3Mask) /* File type information */ FILE *console; int fd_width; int Fvwm_fd[2]; int x_fd; /* X related things */ Display *dpy; Window Root, win; int screen,d_depth,ScreenWidth,ScreenHeight; Pixel back, fore; GC graph,shadow,hilite; XFontStruct *ButtonFont; int fontheight; static Atom wm_del_win; Atom MwmAtom = None; /* Module related information */ char *Module; int WindowIsUp=0,win_width=5,win_height=5,win_grav,win_x,win_y,win_title,win_border; int Clength,Transient=0,Pressed=0,ButPressed,Checked=0; ButtonArray buttons; List windows; char *ClickAction[3]={"Iconify -1,Raise","Iconify","Lower"},*EnterAction, *BackColor = "white",*ForeColor = "black",*geometry=""; char *font_string = "fixed"; int UseSkipList=0,Anchor=1,UseIconNames=0; /****************************************************************************** Main - Setup the XConnection,request the window list and loop forever Based on main() from FvwmIdent: Copyright 1994, Robert Nation and Nobutaka Suzuki. ******************************************************************************/ void main(int argc, char **argv) { char *temp, *s; /* Open the console for messages */ OpenConsole(); /* Save the program name for error messages and config parsing */ temp = argv[0]; s=strrchr(argv[0], '/'); if (s != NULL) temp = s + 1; /* Setup my name */ Module = safemalloc(strlen(temp)+2); strcpy(Module,"*"); strcat(Module, temp); Clength = strlen(Module); if((argc != 6)&&(argc != 7)) { fprintf(stderr,"%s Version %s should only be executed by fvwm!\n",Module, VERSION); ConsoleMessage("%s Version %s should only be executed by fvwm!\n",Module, VERSION); exit(1); } if ((argc==7)&&(!mystrcasecmp(argv[6],"Transient"))) Transient=1; Fvwm_fd[0] = atoi(argv[1]); Fvwm_fd[1] = atoi(argv[2]); signal (SIGPIPE, DeadPipe); /* Parse the config file */ ParseConfig(argv[3]); /* Setup the XConnection */ StartMeUp(); InitArray(&buttons,0,0,win_width,fontheight+6); InitList(&windows); fd_width = GetFdWidth(); /* Request a list of all windows, * wait for ConfigureWindow packets */ SendFvwmPipe("Send_WindowList",0); /* Recieve all messages from Fvwm */ EndLessLoop(); } /****************************************************************************** EndLessLoop - Read and redraw until we get killed, blocking when can't read ******************************************************************************/ void EndLessLoop() { fd_set readset; struct timeval tv; while(1) { FD_ZERO(&readset); FD_SET(Fvwm_fd[1],&readset); FD_SET(x_fd,&readset); tv.tv_sec=0; tv.tv_usec=0; #ifdef __hpux if (!select(fd_width,(int *)&readset,NULL,NULL,&tv)) { XPending(dpy); FD_ZERO(&readset); FD_SET(Fvwm_fd[1],&readset); FD_SET(x_fd,&readset); select(fd_width,(int *)&readset,NULL,NULL,NULL); } #else if (!select(fd_width,&readset,NULL,NULL,&tv)) { XPending(dpy); FD_ZERO(&readset); FD_SET(Fvwm_fd[1],&readset); FD_SET(x_fd,&readset); select(fd_width,&readset,NULL,NULL,NULL); } #endif if (FD_ISSET(x_fd,&readset)) LoopOnEvents(); if (!FD_ISSET(Fvwm_fd[1],&readset)) continue; ReadFvwmPipe(); } } /****************************************************************************** ReadFvwmPipe - Read a single message from the pipe from Fvwm Originally Loop() from FvwmIdent: Copyright 1994, Robert Nation and Nobutaka Suzuki. ******************************************************************************/ void ReadFvwmPipe() { int count,total,count2=0,body_length; unsigned long header[3],*body; char *cbody; if(ReadFvwmPacket(Fvwm_fd[1],header,&body) > 0) { ProcessMessage(header[1],body); free(body); } } /****************************************************************************** ProcessMessage - Process the message coming from Fvwm Skeleton based on processmessage() from FvwmIdent: Copyright 1994, Robert Nation and Nobutaka Suzuki. ******************************************************************************/ void ProcessMessage(unsigned long type,unsigned long *body) { int redraw=0,i; long flags; char *name,*string; switch(type) { case M_ADD_WINDOW: case M_CONFIGURE_WINDOW: if (body[0]==win) { win_x=(int)body[3]; win_y=(int)body[4]; win_title=(int)body[9]; win_border=(int)body[10]; if (win_border!=0) win_border++; } if (FindItem(&windows,body[0])!=-1) break; if (!(body[8]&WINDOWLISTSKIP) || !UseSkipList) AddItem(&windows,body[0],body[8]); break; case M_DESTROY_WINDOW: if ((i=DeleteItem(&windows,body[0]))==-1) break; RemoveButton(&buttons,i); if (WindowIsUp) AdjustWindow(); redraw=1; break; case M_WINDOW_NAME: case M_ICON_NAME: if ((type==M_ICON_NAME && !UseIconNames) || (type==M_WINDOW_NAME && UseIconNames)) break; if ((i=UpdateItemName(&windows,body[0],(char *)&body[3]))==-1) break; string=(char *)&body[3]; name=makename(string,ItemFlags(&windows,body[0])); if (UpdateButton(&buttons,i,name,-1)==-1) AddButton(&buttons,name,1); free(name); if (WindowIsUp) AdjustWindow(); redraw=1; break; case M_DEICONIFY: case M_ICONIFY: if ((i=FindItem(&windows,body[0]))==-1) break; flags=ItemFlags(&windows,body[0]); if (type==M_DEICONIFY && !(flags&ICONIFIED)) break; if (type==M_ICONIFY && flags&ICONIFIED) break; flags^=ICONIFIED; UpdateItemFlags(&windows,body[0],flags); string=ItemName(&windows,i); name=makename(string,flags); if (UpdateButton(&buttons,i,name,-1)!=-1) redraw=1; free(name); break; case M_END_WINDOWLIST: if (!WindowIsUp) MakeMeWindow(); redraw=1; break; case M_NEW_DESK: break; case M_NEW_PAGE: break; } if (redraw && WindowIsUp==1) RedrawWindow(0); } /****************************************************************************** SendFvwmPipe - Send a message back to fvwm Based on SendInfo() from FvwmIdent: Copyright 1994, Robert Nation and Nobutaka Suzuki. ******************************************************************************/ void SendFvwmPipe(char *message,unsigned long window) { int w; char *hold,*temp,*temp_msg; hold=message; while(1) { temp=strchr(hold,','); if (temp!=NULL) { temp_msg=malloc(temp-hold+1); strncpy(temp_msg,hold,(temp-hold)); temp_msg[(temp-hold)]='\0'; hold=temp+1; } else temp_msg=hold; write(Fvwm_fd[0],&window, sizeof(unsigned long)); w=strlen(temp_msg); write(Fvwm_fd[0],&w,sizeof(int)); write(Fvwm_fd[0],temp_msg,w); /* keep going */ w=1; write(Fvwm_fd[0],&w,sizeof(int)); if(temp_msg!=hold) free(temp_msg); else break; } } /*********************************************************************** Detected a broken pipe - time to exit Based on DeadPipe() from FvwmIdent: Copyright 1994, Robert Nation and Nobutaka Suzuki. **********************************************************************/ void DeadPipe(int nonsense) { ShutMeDown(1); } /****************************************************************************** WaitForExpose - Used to wait for expose event so we don't draw too early ******************************************************************************/ void WaitForExpose(void) { XEvent Event; while(1) { XNextEvent(dpy,&Event); if (Event.type==Expose) { if (Event.xexpose.count==0) break; } } } /****************************************************************************** RedrawWindow - Update the needed lines and erase any old ones ******************************************************************************/ void RedrawWindow(int force) { DrawButtonArray(&buttons,force); if (XQLength(dpy) && !force) LoopOnEvents(); } /****************************************************************************** ConsoleMessage - Print a message on the console. Works like printf. ******************************************************************************/ void ConsoleMessage(char *fmt, ...) { #ifndef NO_CONSOLE va_list args; FILE *filep; if (console==NULL) filep=stderr; else filep=console; va_start(args,fmt); vfprintf(filep,fmt,args); va_end(args); #endif } /****************************************************************************** OpenConsole - Open the console as a way of sending messages ******************************************************************************/ int OpenConsole() { #ifndef NO_CONSOLE if ((console=fopen("/dev/console","w"))==NULL) { fprintf(stderr,"%s: cannot open console\n",Module); return 0; } #endif return 1; } /****************************************************************************** ParseConfig - Parse the configuration file fvwm to us to use Based on part of main() from FvwmIdent: Copyright 1994, Robert Nation and Nobutaka Suzuki. ******************************************************************************/ void ParseConfig(char *file) { char line[256]; char *tline; FILE *ptr; ptr=fopen(file,"r"); if(ptr != (FILE *)NULL) { tline = fgets(line,(sizeof line)-1,ptr); while(tline != (char *)0) { while(isspace(*tline))tline++; if(strlen(tline)>1) { if(mystrncasecmp(tline, CatString3(Module, "Font",""),Clength+4)==0) CopyString(&font_string,&tline[Clength+4]); else if(mystrncasecmp(tline,CatString3(Module,"Fore",""), Clength+4)==0) CopyString(&ForeColor,&tline[Clength+4]); else if(mystrncasecmp(tline,CatString3(Module, "Geometry",""), Clength+8)==0) CopyString(&geometry,&tline[Clength+8]); else if(mystrncasecmp(tline,CatString3(Module, "Back",""), Clength+4)==0) CopyString(&BackColor,&tline[Clength+4]); else if(mystrncasecmp(tline,CatString3(Module, "NoAnchor",""), Clength+8)==0) Anchor=0; else if(mystrncasecmp(tline,CatString3(Module, "Action",""), Clength+6)==0) LinkAction(&tline[Clength+6]); else if(mystrncasecmp(tline,CatString3(Module, "UseSkipList",""), Clength+11)==0) UseSkipList=1; else if(mystrncasecmp(tline,CatString3(Module, "UseIconNames",""), Clength+12)==0) UseIconNames=1; } tline = fgets(line,(sizeof line)-1,ptr); } } } /****************************************************************************** LoopOnEvents - Process all the X events we get ******************************************************************************/ void LoopOnEvents() { int num; char buffer[10]; XEvent Event; Window dummyroot,dummychild; int x,x1,y,y1; unsigned int dummy1; if (Transient && !Checked) { XQueryPointer(dpy,win,&dummyroot,&dummychild,&x1,&y1,&x,&y,&dummy1); num=WhichButton(&buttons,x,y); if (num!=-1) { Pressed=1; ButPressed=num; SwitchButton(&buttons,num); } else Pressed=0; Checked=1; } while(XPending(dpy)) { XNextEvent(dpy,&Event); switch(Event.type) { case ButtonRelease: if (Pressed) { num=WhichButton(&buttons,Event.xbutton.x,Event.xbutton.y); if (num!=-1) { SendFvwmPipe(ClickAction[(Transient) ? 0:Event.xbutton.button-1], ItemID(&windows,num)); SwitchButton(&buttons,num); } } if (Transient) ShutMeDown(0); Pressed=0; ButPressed=-1; break; case ButtonPress: num=WhichButton(&buttons,Event.xbutton.x,Event.xbutton.y); if (num != -1) { SwitchButton(&buttons,num); ButPressed=num; } else ButPressed=-1; Pressed=1; break; case Expose: if (Event.xexpose.count==0) RedrawWindow(1); break; case KeyPress: num=XLookupString(&Event.xkey,buffer,10,NULL,0); if (num==1) { if (buffer[0]=='q' || buffer[0]=='Q') ShutMeDown(0); else if (buffer[0]=='i' || buffer[0]=='I') PrintList(&windows); else if (buffer[0]=='b' || buffer[0]=='B') PrintButtons(&buttons); } break; case ClientMessage: if ((Event.xclient.format==32) && (Event.xclient.data.l[0]==wm_del_win)) ShutMeDown(0); case EnterNotify: if (!SomeButtonDown(Event.xcrossing.state)) break; num=WhichButton(&buttons,Event.xcrossing.x,Event.xcrossing.y); if (num!=-1) { SwitchButton(&buttons,num); ButPressed=num; } else ButPressed=-1; Pressed=1; break; case LeaveNotify: if (!SomeButtonDown(Event.xcrossing.state)) break; if (ButPressed!=-1) SwitchButton(&buttons,ButPressed); Pressed=0; break; case MotionNotify: if (!Pressed) break; num=WhichButton(&buttons,Event.xmotion.x,Event.xmotion.y); if (num==ButPressed) break; if (ButPressed!=-1) SwitchButton(&buttons,ButPressed); if (num!=-1) { SwitchButton(&buttons,num); ButPressed=num; } else ButPressed=-1; break; } } } /****************************************************************************** AdjustWindow - Resize the window according to maxwidth by number of buttons ******************************************************************************/ void AdjustWindow() { int new_width=0,new_height=0,tw,i,total; char *temp; total=ItemCount(&windows); if (!total) { if (WindowIsUp==1) { XUnmapWindow(dpy,win); WindowIsUp=2; } return; } for(i=0;i0) win_height=new_height; if (new_width>0) win_width=new_width; if (WindowIsUp==2) { XMapWindow(dpy,win); WindowIsUp=1; WaitForExpose(); } } /****************************************************************************** makename - Based on the flags return me '(name)' or 'name' ******************************************************************************/ char *makename(char *string,long flags) { char *ptr; ptr=safemalloc(strlen(string)+3); if (flags&ICONIFIED) strcpy(ptr,"("); else strcpy(ptr,""); strcat(ptr,string); if (flags&ICONIFIED) strcat(ptr,")"); return ptr; } /****************************************************************************** LinkAction - Link an response to a users action ******************************************************************************/ void LinkAction(char *string) { char *temp; temp=string; while(isspace(*temp)) temp++; if(mystrncasecmp(temp, "Click1", 6)==0) CopyString(&ClickAction[0],&temp[6]); else if(mystrncasecmp(temp, "Click2", 6)==0) CopyString(&ClickAction[1],&temp[6]); else if(mystrncasecmp(temp, "Click3", 6)==0) CopyString(&ClickAction[2],&temp[6]); else if(mystrncasecmp(temp, "Enter", 5)==0) CopyString(&EnterAction,&temp[5]); } /****************************************************************************** MakeMeWindow - Create and setup the window we will need ******************************************************************************/ void MakeMeWindow(void) { XSizeHints hints; XGCValues gcval; unsigned long gcmask; unsigned int dummy1,dummy2; int x,y,ret,count; Window dummyroot,dummychild; if ((count=ItemCount(&windows))==0 && Transient) ShutMeDown(0); AdjustWindow(); hints.width=win_width; hints.height=win_height; hints.win_gravity=NorthWestGravity; hints.flags=PSize|PWinGravity|PResizeInc; hints.width_inc=0; hints.height_inc=0; if (geometry!= NULL) { ret=XParseGeometry(geometry,&x,&y,&dummy1,&dummy2); if (ret&XValue && ret &YValue) { hints.x=x; if (ret&XNegative) hints.x+=XDisplayWidth(dpy,screen)-win_width; hints.y=y; if (ret&YNegative) hints.y+=XDisplayHeight(dpy,screen)-win_height; hints.flags|=USPosition; } if (ret&XNegative) { if (ret&YNegative) hints.win_gravity=SouthEastGravity; else hints.win_gravity=NorthEastGravity; } else { if (ret&YNegative) hints.win_gravity=SouthWestGravity; else hints.win_gravity=NorthWestGravity; } } if (Transient) { XQueryPointer(dpy,Root,&dummyroot,&dummychild,&hints.x,&hints.y,&x,&y,&dummy1); hints.win_gravity=NorthWestGravity; hints.flags |= USPosition; } win_grav=hints.win_gravity; win_x=hints.x; win_y=hints.y; if(d_depth < 2) { back = GetColor("white"); fore = GetColor("black"); } else { back = GetColor(BackColor); fore = GetColor(ForeColor); } win=XCreateSimpleWindow(dpy,Root,hints.x,hints.y,hints.width,hints.height,1, fore,back); wm_del_win=XInternAtom(dpy,"WM_DELETE_WINDOW",False); XSetWMProtocols(dpy,win,&wm_del_win,1); XSetWMNormalHints(dpy,win,&hints); if (!Transient) { XGrabButton(dpy,1,AnyModifier,win,True,GRAB_EVENTS,GrabModeAsync, GrabModeAsync,None,None); XGrabButton(dpy,2,AnyModifier,win,True,GRAB_EVENTS,GrabModeAsync, GrabModeAsync,None,None); XGrabButton(dpy,3,AnyModifier,win,True,GRAB_EVENTS,GrabModeAsync, GrabModeAsync,None,None); SetMwmHints(MWM_DECOR_ALL|MWM_DECOR_RESIZEH|MWM_DECOR_MAXIMIZE|MWM_DECOR_MINIMIZE, MWM_FUNC_ALL|MWM_FUNC_RESIZE|MWM_FUNC_MAXIMIZE|MWM_FUNC_MINIMIZE, MWM_INPUT_MODELESS); } else { SetMwmHints(0,MWM_FUNC_ALL,MWM_INPUT_MODELESS); } gcval.foreground=fore; gcval.background=back; gcval.font=ButtonFont->fid; gcmask=GCForeground|GCBackground|GCFont; graph=XCreateGC(dpy,Root,gcmask,&gcval); if(d_depth < 2) gcval.foreground=GetShadow(fore); else gcval.foreground=GetShadow(back); gcval.background=back; gcmask=GCForeground|GCBackground; shadow=XCreateGC(dpy,Root,gcmask,&gcval); gcval.foreground=GetHilite(back); gcval.background=back; gcmask=GCForeground|GCBackground; hilite=XCreateGC(dpy,Root,gcmask,&gcval); XSelectInput(dpy,win,(ExposureMask | KeyPressMask)); ChangeWindowName(&Module[1]); if (ItemCount(&windows)>0) { XMapRaised(dpy,win); WaitForExpose(); WindowIsUp=1; } else WindowIsUp=2; if (Transient) { if ( XGrabPointer(dpy,win,True,GRAB_EVENTS,GrabModeAsync,GrabModeAsync, None,None,CurrentTime)!=GrabSuccess) ShutMeDown(1); XQueryPointer(dpy,Root,&dummyroot,&dummychild,&hints.x,&hints.y,&x,&y,&dummy1); if (!SomeButtonDown(dummy1)) ShutMeDown(0); } } /****************************************************************************** StartMeUp - Do X initialization things ******************************************************************************/ void StartMeUp() { if (!(dpy = XOpenDisplay(""))) { fprintf(stderr,"%s: can't open display %s", Module, XDisplayName("")); exit (1); } x_fd = XConnectionNumber(dpy); screen= DefaultScreen(dpy); Root = RootWindow(dpy, screen); d_depth = DefaultDepth(dpy, screen); ScreenHeight = DisplayHeight(dpy,screen); ScreenWidth = DisplayWidth(dpy,screen); if ((ButtonFont=XLoadQueryFont(dpy,font_string))==NULL) { if ((ButtonFont=XLoadQueryFont(dpy,"fixed"))==NULL) exit(1); } fontheight=ButtonFont->ascent+ButtonFont->descent; win_width=XTextWidth(ButtonFont,"XXXXXXXXXXXXXXX",10); } /****************************************************************************** ShutMeDown - Do X client cleanup ******************************************************************************/ void ShutMeDown(int exitstat) { FreeList(&windows); FreeAllButtons(&buttons); XFreeGC(dpy,graph); if (WindowIsUp) XDestroyWindow(dpy,win); XCloseDisplay(dpy); exit(exitstat); } /****************************************************************************** ChangeWindowName - Self explanitory Original work from FvwmIdent: Copyright 1994, Robert Nation and Nobutaka Suzuki. ******************************************************************************/ void ChangeWindowName(char *str) { XTextProperty name; if (XStringListToTextProperty(&str,1,&name) == 0) { fprintf(stderr,"%s: cannot allocate window name.\n",Module); return; } XSetWMName(dpy,win,&name); XSetWMIconName(dpy,win,&name); XFree(name.value); } /************************************************************************** * * Sets mwm hints * *************************************************************************/ /* * Now, if we (hopefully) have MWW - compatible window manager , * say, mwm, ncdwm, or else, we will set useful decoration style. * Never check for MWM_RUNNING property.May be considered bad. */ void SetMwmHints(unsigned int value, unsigned int funcs, unsigned int input) { PropMwmHints prop; if (MwmAtom==None) { MwmAtom=XInternAtom(dpy,"_MOTIF_WM_HINTS",False); } if (MwmAtom!=None) { /* sh->mwm.decorations contains OR of the MWM_DECOR_XXXXX */ prop.decorations= value; prop.functions = funcs; prop.inputMode = input; prop.flags = MWM_HINTS_DECORATIONS| MWM_HINTS_FUNCTIONS | MWM_HINTS_INPUT_MODE; /* HOP - LA! */ XChangeProperty (dpy,win, MwmAtom, MwmAtom, 32, PropModeReplace, (unsigned char *)&prop, PROP_MWM_HINTS_ELEMENTS); } }