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