1 /**
2  * @file swallow.c
3  * @author Joe Wingbermuehle
4  * @date 2005-2006
5  *
6  * @brief Swallow tray component.
7  *
8  */
9 
10 #include "jwm.h"
11 #include "swallow.h"
12 #include "main.h"
13 #include "tray.h"
14 #include "error.h"
15 #include "command.h"
16 #include "color.h"
17 #include "client.h"
18 #include "misc.h"
19 
20 typedef struct SwallowNode {
21 
22    TrayComponentType *cp;
23 
24    char *name;
25    char *command;
26    int border;
27    int userWidth;
28    int userHeight;
29 
30    struct SwallowNode *next;
31 
32 } SwallowNode;
33 
34 static SwallowNode *pendingNodes = NULL;
35 static SwallowNode *swallowNodes = NULL;
36 
37 static void ReleaseNodes(SwallowNode *nodes);
38 static void Destroy(TrayComponentType *cp);
39 static void Resize(TrayComponentType *cp);
40 
41 /** Start swallow processing. */
StartupSwallow(void)42 void StartupSwallow(void)
43 {
44    SwallowNode *np;
45    for(np = pendingNodes; np; np = np->next) {
46       if(np->command) {
47          RunCommand(np->command);
48       }
49    }
50 }
51 
52 /** Destroy swallow data. */
DestroySwallow(void)53 void DestroySwallow(void)
54 {
55    ReleaseNodes(pendingNodes);
56    ReleaseNodes(swallowNodes);
57    pendingNodes = NULL;
58    swallowNodes = NULL;
59 }
60 
61 /** Release a linked list of swallow nodes. */
ReleaseNodes(SwallowNode * nodes)62 void ReleaseNodes(SwallowNode *nodes)
63 {
64    while(nodes) {
65       SwallowNode *np = nodes->next;
66       Assert(nodes->name);
67       Release(nodes->name);
68       if(nodes->command) {
69          Release(nodes->command);
70       }
71       Release(nodes);
72       nodes = np;
73    }
74 }
75 
76 /** Create a swallowed application tray component. */
CreateSwallow(const char * name,const char * command,int width,int height)77 TrayComponentType *CreateSwallow(const char *name, const char *command,
78                                  int width, int height)
79 {
80 
81    TrayComponentType *cp;
82    SwallowNode *np;
83 
84    if(JUNLIKELY(!name)) {
85       Warning(_("cannot swallow a client with no name"));
86       return NULL;
87    }
88 
89    np = Allocate(sizeof(SwallowNode));
90    np->name = CopyString(name);
91    np->command = CopyString(command);
92 
93    np->next = pendingNodes;
94    pendingNodes = np;
95 
96    cp = CreateTrayComponent();
97    np->cp = cp;
98    cp->object = np;
99    cp->Destroy = Destroy;
100    cp->Resize = Resize;
101 
102    if(width) {
103       cp->requestedWidth = width;
104       np->userWidth = 1;
105    } else {
106       cp->requestedWidth = 1;
107       np->userWidth = 0;
108    }
109    if(height) {
110       cp->requestedHeight = height;
111       np->userHeight = 1;
112    } else {
113       cp->requestedHeight = 1;
114       np->userHeight = 0;
115    }
116 
117    return cp;
118 
119 }
120 
121 /** Process an event on a swallowed window. */
ProcessSwallowEvent(const XEvent * event)122 char ProcessSwallowEvent(const XEvent *event)
123 {
124 
125    SwallowNode *np;
126    int width, height;
127 
128    for(np = swallowNodes; np; np = np->next) {
129       if(event->xany.window == np->cp->window) {
130          switch(event->type) {
131          case DestroyNotify:
132             np->cp->window = None;
133             np->cp->requestedWidth = 1;
134             np->cp->requestedHeight = 1;
135             ResizeTray(np->cp->tray);
136             break;
137          case ResizeRequest:
138             np->cp->requestedWidth
139                = event->xresizerequest.width + np->border * 2;
140             np->cp->requestedHeight
141                = event->xresizerequest.height + np->border * 2;
142             ResizeTray(np->cp->tray);
143             break;
144          case ConfigureNotify:
145             /* I don't think this should be necessary, but somehow
146              * resize requests slip by sometimes... */
147             width = event->xconfigure.width + np->border * 2;
148             height = event->xconfigure.height + np->border * 2;
149             if(   width != np->cp->requestedWidth
150                && height != np->cp->requestedHeight) {
151                np->cp->requestedWidth = width;
152                np->cp->requestedHeight = height;
153                ResizeTray(np->cp->tray);
154             }
155             break;
156          default:
157             break;
158          }
159          return 1;
160       }
161    }
162 
163    return 0;
164 
165 }
166 
167 /** Handle a tray resize. */
Resize(TrayComponentType * cp)168 void Resize(TrayComponentType *cp)
169 {
170 
171 
172    SwallowNode *np = (SwallowNode*)cp->object;
173 
174    if(cp->window != None) {
175       const unsigned int width = cp->width - np->border * 2;
176       const unsigned int height = cp->height - np->border * 2;
177       JXResizeWindow(display, cp->window, width, height);
178    }
179 
180 }
181 
182 /** Destroy a swallow tray component. */
Destroy(TrayComponentType * cp)183 void Destroy(TrayComponentType *cp)
184 {
185 
186    /* Destroy the window if there is one. */
187    if(cp->window) {
188 
189       JXReparentWindow(display, cp->window, rootWindow, 0, 0);
190       JXRemoveFromSaveSet(display, cp->window);
191 
192       ClientState state;
193       memset(&state, 0, sizeof(state));
194       ReadWMProtocols(cp->window, &state);
195       if(state.status & STAT_DELETE) {
196          SendClientMessage(cp->window, ATOM_WM_PROTOCOLS,
197                            ATOM_WM_DELETE_WINDOW);
198       } else {
199          JXKillClient(display, cp->window);
200       }
201 
202    }
203 
204 }
205 
206 /** Determine if this is a window to be swallowed, if it is, swallow it. */
CheckSwallowMap(Window win)207 char CheckSwallowMap(Window win)
208 {
209 
210    SwallowNode **npp;
211    XClassHint hint;
212    XWindowAttributes attr;
213    char result;
214 
215    /* Return if there are no programs left to swallow. */
216    if(!pendingNodes) {
217       return 0;
218    }
219 
220    /* Get the name of the window. */
221    if(JXGetClassHint(display, win, &hint) == 0) {
222       return 0;
223    }
224 
225    /* Check if we should swallow this window. */
226    result = 0;
227    npp = &pendingNodes;
228    while(*npp) {
229 
230       SwallowNode *np = *npp;
231       Assert(np->cp->tray->window != None);
232 
233       if(!strcmp(hint.res_name, np->name)) {
234 
235          /* Swallow the window. */
236          JXSelectInput(display, win,
237                        StructureNotifyMask | ResizeRedirectMask);
238          JXAddToSaveSet(display, win);
239          JXSetWindowBorder(display, win, colors[COLOR_TRAY_BG2]);
240          JXReparentWindow(display, win,
241                           np->cp->tray->window, 0, 0);
242          JXMapRaised(display, win);
243          np->cp->window = win;
244 
245          /* Remove this node from the pendingNodes list and place it
246           * on the swallowNodes list. */
247          *npp = np->next;
248          np->next = swallowNodes;
249          swallowNodes = np;
250 
251          /* Update the size. */
252          JXGetWindowAttributes(display, win, &attr);
253          np->border = attr.border_width;
254          if(!np->userWidth) {
255             np->cp->requestedWidth = attr.width + 2 * np->border;
256          }
257          if(!np->userHeight) {
258             np->cp->requestedHeight = attr.height + 2 * np->border;
259          }
260 
261          ResizeTray(np->cp->tray);
262          result = 1;
263 
264          break;
265       }
266 
267       npp = &np->next;
268 
269    }
270    JXFree(hint.res_name);
271    JXFree(hint.res_class);
272 
273    return result;
274 
275 }
276 
277 /** Determine if there are swallow processes pending. */
IsSwallowPending(void)278 char IsSwallowPending(void)
279 {
280    return pendingNodes ? 1 : 0;
281 }
282 
283