]> git.decadent.org.uk Git - ion3.git/blob - mod_sm/sm_matchwin.c
Imported Upstream version 20090110
[ion3.git] / mod_sm / sm_matchwin.c
1 /*
2  * ion/mod_sm/sm_mathcwin.c
3  *
4  * Copyright (c) Tuomo Valkonen 2004-2009. 
5  * 
6  * Based on the code of the 'sm' module for Ion1 by an unknown contributor.
7  *
8  * This is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  */
13
14 #include <string.h>
15 #include <unistd.h>
16 #include <time.h>
17 #include <X11/Xatom.h>
18
19 #include <libmainloop/signal.h>
20 #include <ioncore/common.h>
21 #include <ioncore/global.h>
22 #include <ioncore/exec.h>
23 #include <ioncore/names.h>
24 #include <ioncore/property.h>
25
26 #include "sm_matchwin.h"
27
28 #define TIME_OUT 60000
29
30 static WWinMatch *match_list=NULL;
31 static WTimer *purge_timer=NULL;
32
33
34 char *mod_sm_get_window_role(Window window)
35 {
36     Atom atom;
37     XTextProperty tp;
38     
39     atom=XInternAtom(ioncore_g.dpy, "WM_WINDOW_ROLE", False);
40     
41     if(XGetTextProperty(ioncore_g.dpy, window, &tp, atom))
42     {
43         if(tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
44             return((char *)tp.value);
45     }
46     
47     return NULL;
48 }
49
50 Window mod_sm_get_client_leader(Window window)
51 {
52     Window client_leader = 0;
53     Atom atom;
54     Atom actual_type;
55     int actual_format;
56     unsigned long nitems;
57     unsigned long bytes_after;
58     unsigned char *prop = NULL;
59     
60     atom=XInternAtom(ioncore_g.dpy, "WM_CLIENT_LEADER", False);
61     
62     if(XGetWindowProperty(ioncore_g.dpy, window, atom,
63                           0L, 1L, False, AnyPropertyType, &actual_type,
64                           &actual_format, &nitems, &bytes_after,
65                           &prop) == Success)
66     {
67         if(actual_type == XA_WINDOW && actual_format == 32
68            && nitems == 1       && bytes_after == 0)
69             client_leader=*((Window *)prop);
70         XFree(prop);
71     }
72     return client_leader;
73 }
74
75 char *mod_sm_get_client_id(Window window)
76 {
77     char *client_id = NULL;
78     Window client_leader;
79     XTextProperty tp;
80     Atom atom;
81     
82     if((client_leader=mod_sm_get_client_leader(window))!=0){
83         atom=XInternAtom(ioncore_g.dpy, "SM_CLIENT_ID", False);         
84         if (XGetTextProperty (ioncore_g.dpy, client_leader, &tp, atom))
85             if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
86                 client_id = (char *) tp.value;
87     }
88     
89     return client_id;
90 }
91
92 char *mod_sm_get_window_cmd(Window window)
93 {
94     char **cmd_argv, *command=NULL;
95     int id, i, len=0, cmd_argc=0;
96     
97     if(XGetCommand(ioncore_g.dpy, window, &cmd_argv, &cmd_argc) && (cmd_argc > 0))
98         ;
99     else if((id=mod_sm_get_client_leader(window)))
100         XGetCommand(ioncore_g.dpy, id, &cmd_argv, &cmd_argc);
101     
102     if(cmd_argc > 0){
103         for(i=0; i < cmd_argc; i++)
104             len+=strlen(cmd_argv[i])+1;
105         command=ALLOC_N(char, len+1);
106         strcpy(command, cmd_argv[0]);
107         for(i=1; i < cmd_argc; i++){
108             strcat(command, " ");
109             strcat(command, cmd_argv[i]);
110         }
111         XFreeStringList(cmd_argv);
112     }
113     
114     return command;
115 }
116
117 static void free_win_match(WWinMatch *match)
118 {       
119     UNLINK_ITEM(match_list, match, next, prev);
120     
121     if(match->pholder!=NULL)
122         destroy_obj((Obj*)match->pholder);
123     
124     if(match->client_id)
125         free(match->client_id);
126     if(match->window_role)
127         free(match->window_role);
128     if(match->wclass)
129         free(match->wclass);
130     if(match->wm_name)
131         free(match->wm_name);
132     if(match->wm_cmd)
133         free(match->wm_cmd);            
134     free(match);
135 }
136
137 static void mod_sm_purge_matches(WTimer *timer)
138 {
139     assert(timer==purge_timer);
140     purge_timer=NULL;
141     destroy_obj((Obj*)timer);
142     
143 #ifdef DEBUG
144     warn("purging remaining matches\n");
145 #endif
146     while(match_list)
147         free_win_match(match_list);
148 }
149
150 void mod_sm_start_purge_timer()
151 {
152     if(purge_timer==NULL)
153         purge_timer=create_timer();
154     if(purge_timer!=NULL){
155         timer_set(purge_timer, TIME_OUT, 
156                   (WTimerHandler*)mod_sm_purge_matches, NULL);
157     }
158 }
159
160 void mod_sm_register_win_match(WWinMatch *match)
161 {
162     LINK_ITEM(match_list, match, next, prev);
163 }
164
165 bool mod_sm_have_match_list()
166 {
167     if(match_list!=NULL)
168         return TRUE;
169     else
170         return FALSE;
171 }
172
173 #define xstreq(a,b) (a && b && (strcmp(a,b)==0))
174
175 /* Tries to match a window against a list of loaded matches */
176
177 static WWinMatch *match_cwin(WClientWin *cwin)
178 {
179     WWinMatch *match=match_list;
180     int win_match;
181     XClassHint clss;
182     char *client_id=mod_sm_get_client_id(cwin->win);
183     char *window_role=mod_sm_get_window_role(cwin->win);
184     char *wm_cmd=mod_sm_get_window_cmd(cwin->win);
185     char **wm_name=NULL;
186     int n;
187     
188     wm_name=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n);
189     if(n<=0)
190         assert(wm_name==NULL);
191     
192     XGetClassHint(ioncore_g.dpy, cwin->win, &clss);
193     
194     for(; match!=NULL; match=match->next){
195         
196         win_match=0;
197         
198         if(client_id || match->client_id){
199             if(xstreq(match->client_id, client_id)){
200                 win_match+=2;
201                 if(xstreq(match->window_role, window_role))
202                     win_match++;
203             }
204         }
205         if(win_match<3){
206             if(xstreq(match->wclass, clss.res_class) && xstreq(match->winstance, clss.res_name)){
207                 win_match++;
208                 if(win_match<3){
209                     if(xstreq(match->wm_cmd, wm_cmd))
210                         win_match++;
211                     if(wm_name!=NULL && *wm_name!=NULL && 
212                        xstreq(match->wm_name, *wm_name)){
213                         win_match++;
214                     }
215                 }
216             }
217         }
218         if(win_match>2)
219             break;              
220     }
221     XFree(client_id);
222     XFree(window_role);
223     XFreeStringList(wm_name);
224     free(wm_cmd);
225     return match;
226 }
227
228 /* Returns frame_id of a match. Called from add_clientwin_alt in sm.c */
229
230 WPHolder *mod_sm_match_cwin_to_saved(WClientWin *cwin)
231 {
232     WWinMatch *match=NULL;
233     WPHolder *ph=NULL;
234
235     if((match=match_cwin(cwin))){
236         ph=match->pholder;
237         match->pholder=NULL;
238         free_win_match(match);
239     }
240     
241     return ph;
242 }
243
244
245 bool mod_sm_add_match(WPHolder *ph, ExtlTab tab)
246 {
247     WWinMatch *m=NULL;
248     
249     m=ALLOC(WWinMatch);
250     if(m==NULL)
251         return FALSE;
252     
253     m->client_id=NULL;
254     m->window_role=NULL;
255     m->winstance=NULL;
256     m->wclass=NULL;
257     m->wm_name=NULL;
258     m->wm_cmd=NULL;
259     
260     extl_table_gets_s(tab, "mod_sm_client_id", &(m->client_id));
261     extl_table_gets_s(tab, "mod_sm_window_role", &(m->window_role));
262     extl_table_gets_s(tab, "mod_sm_wclass", &(m->wclass));
263     extl_table_gets_s(tab, "mod_sm_winstance", &(m->winstance));
264     extl_table_gets_s(tab, "mod_sm_wm_name", &(m->wm_name));
265     extl_table_gets_s(tab, "mod_sm_wm_cmd", &(m->wm_cmd));
266     
267     m->pholder=ph;
268     
269     mod_sm_register_win_match(m);
270
271     return TRUE;
272 }
273
274
275 void mod_sm_get_configuration(WClientWin *cwin, ExtlTab tab)
276 {
277     XClassHint clss;
278     char *client_id=NULL, *window_role=NULL, *wm_cmd=NULL, **wm_name;
279     int n=0;
280     
281     if((client_id=mod_sm_get_client_id(cwin->win))){
282         extl_table_sets_s(tab, "mod_sm_client_id", client_id);
283         XFree(client_id);
284     }
285      
286     
287     if((window_role=mod_sm_get_window_role(cwin->win))){
288         extl_table_sets_s(tab, "mod_sm_window_role", window_role);
289         XFree(window_role);
290     }
291     
292     if(XGetClassHint(ioncore_g.dpy, cwin->win, &clss) != 0){
293         extl_table_sets_s(tab, "mod_sm_wclass", clss.res_class);
294         extl_table_sets_s(tab, "mod_sm_winstance", clss.res_name);
295     }
296     
297     wm_name=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n);
298
299     if(n>0 && wm_name!=NULL){
300         extl_table_sets_s(tab, "mod_sm_wm_name", *wm_name);
301         XFreeStringList(wm_name);
302         
303     }
304     
305     if((wm_cmd=mod_sm_get_window_cmd(cwin->win))){
306         extl_table_sets_s(tab, "mod_sm_wm_cmd", wm_cmd);
307         free(wm_cmd);
308     }
309 }