merge the C branch into HEAD
[mikachu/openbox.git] / openbox / openboxwrap.c
1 #include "openboxwrap.h"
2 #include "openbox.h"
3 #include "screen.h"
4 #include "prop.h"
5
6 /***************************************************************************
7  
8    Define the type 'OpenboxWrap'
9
10  ***************************************************************************/
11
12 #define IS_OWRAP(v)  ((v)->ob_type == &OpenboxWrapType)
13 #define CHECK_OWRAP(self, funcname) { \
14     if (!IS_OWRAP(self)) { \
15         PyErr_SetString(PyExc_TypeError, \
16                         "descriptor '" funcname "' requires a 'Openbox' " \
17                         "object"); \
18         return NULL; \
19     } \
20 }
21
22
23 staticforward PyTypeObject OpenboxWrapType;
24
25 /***************************************************************************
26  
27    Attribute methods
28  
29  ***************************************************************************/
30
31 static PyObject *owrap_shutdown(OpenboxWrap *self, PyObject *args)
32 {
33     CHECK_OWRAP(self, "shutdown");
34     if (!PyArg_ParseTuple(args, ":shutdown"))
35         return NULL;
36     ob_shutdown = TRUE;
37     Py_INCREF(Py_None);
38     return Py_None;
39 }
40
41 static PyObject *owrap_restart(OpenboxWrap *self, PyObject *args)
42 {
43     char *path = NULL;
44
45     CHECK_OWRAP(self, "restart");
46     if (!PyArg_ParseTuple(args, "|s:restart", &path))
47         return NULL;
48     ob_shutdown = ob_restart = TRUE;
49     ob_restart_path = path;
50     Py_INCREF(Py_None);
51     return Py_None;
52 }
53
54 static PyObject *owrap_state(OpenboxWrap *self, PyObject *args)
55 {
56     CHECK_OWRAP(self, "state");
57     if (!PyArg_ParseTuple(args, ":state"))
58         return NULL;
59     return PyInt_FromLong(ob_state);
60 }
61
62 static PyObject *owrap_desktop(OpenboxWrap *self, PyObject *args)
63 {
64     CHECK_OWRAP(self, "desktop");
65     if (!PyArg_ParseTuple(args, ":desktop"))
66         return NULL;
67     return PyInt_FromLong(screen_desktop);
68 }
69
70 static PyObject *owrap_setDesktop(OpenboxWrap *self, PyObject *args)
71 {
72     int desktop;
73
74     CHECK_OWRAP(self, "setDesktop");
75     if (!PyArg_ParseTuple(args, "i:setDesktop", &desktop))
76         return NULL;
77     if (desktop < 0 || (unsigned)desktop >= screen_num_desktops) {
78         PyErr_SetString(PyExc_ValueError, "invalid desktop");
79         return NULL;
80     }
81     screen_set_desktop(desktop);
82     Py_INCREF(Py_None);
83     return Py_None;
84 }
85
86 static PyObject *owrap_setNextDesktop(OpenboxWrap *self, PyObject *args)
87 {
88     gboolean wrap = TRUE;
89     guint d;
90
91     CHECK_OWRAP(self, "setNextDesktop");
92     if (!PyArg_ParseTuple(args, "|i:setNextDesktop", &wrap))
93         return NULL;
94     d = screen_desktop + 1;
95     if (d >= screen_num_desktops && wrap)
96         d = 0;
97     if (d < screen_num_desktops)
98         screen_set_desktop(d);
99     Py_INCREF(Py_None);
100     return Py_None;
101 }
102
103 static PyObject *owrap_setPreviousDesktop(OpenboxWrap *self, PyObject *args)
104 {
105     gboolean wrap = TRUE;
106     guint d;
107
108     CHECK_OWRAP(self, "setPreviousDesktop");
109     if (!PyArg_ParseTuple(args, "|i:setPreviousDesktop", &wrap))
110         return NULL;
111     d = screen_desktop - 1;
112     if (d >= screen_num_desktops && wrap)
113         d = screen_num_desktops - 1;
114     if (d < screen_num_desktops)
115         screen_set_desktop(d);
116     Py_INCREF(Py_None);
117     return Py_None;
118 }
119
120 static PyObject *owrap_numDesktops(OpenboxWrap *self, PyObject *args)
121 {
122     CHECK_OWRAP(self, "numDesktops");
123     if (!PyArg_ParseTuple(args, ":numDesktops"))
124         return NULL;
125     return PyInt_FromLong(screen_num_desktops);
126 }
127
128 static PyObject *owrap_setNumDesktops(OpenboxWrap *self, PyObject *args)
129 {
130     int desktops;
131
132     CHECK_OWRAP(self, "setNumDesktops");
133     if (!PyArg_ParseTuple(args, "i:setNumDesktops", &desktops))
134         return NULL;
135     if (desktops <= 0) {
136         PyErr_SetString(PyExc_ValueError, "invalid number of desktops");
137         return NULL;
138     }
139     screen_set_num_desktops(desktops);
140     Py_INCREF(Py_None);
141     return Py_None;
142 }
143
144 static PyObject *owrap_desktopNames(OpenboxWrap *self, PyObject *args)
145 {
146     PyObject *tuple;
147     int i, s;
148
149     CHECK_OWRAP(self, "desktopNames");
150     if (!PyArg_ParseTuple(args, ":desktopNames"))
151         return NULL;
152     s = screen_desktop_names->len;
153     tuple = PyTuple_New(s);
154     for (i = 0; i < s; ++i)
155         PyTuple_SET_ITEM(tuple, i, g_ptr_array_index(screen_desktop_names, i));
156     return tuple;
157 }
158
159 static PyObject *owrap_setDesktopNames(OpenboxWrap *self, PyObject *args)
160 {
161     PyObject *seq;
162     int i, s;
163     GPtrArray *data;
164
165     CHECK_OWRAP(self, "setDesktopNames");
166     if (!PyArg_ParseTuple(args, "O:setDesktopNames", &seq))
167         return NULL;
168     if (!PySequence_Check(seq))
169         PyErr_SetString(PyExc_TypeError, "expected a sequence");
170     return NULL;
171
172     s = PySequence_Size(seq);
173     for (i = 0; i < s; ++i) {
174         PyObject *item;
175         gboolean check;
176         item = PySequence_GetItem(seq, i); /* new */
177         check = PyString_Check(item);
178         Py_DECREF(item);
179         if (!check) {
180             PyErr_SetString(PyExc_TypeError, "expected a sequence of strings");
181             return NULL;
182         }
183     }
184
185     data = g_ptr_array_sized_new(s);
186     for (i = 0; i < s; ++i) {
187         PyObject *item;
188         item = PySequence_GetItem(seq, i); /* new */
189         g_ptr_array_index(data, i) = PyString_AsString(item); /* borrowed */
190         Py_DECREF(item);
191     }
192
193     PROP_SETSA(ob_root, net_desktop_names, utf8, data);
194     g_ptr_array_free(data, TRUE);
195
196     Py_INCREF(Py_None);
197     return Py_None;
198 }
199
200 static PyObject *owrap_showingDesktop(OpenboxWrap *self, PyObject *args)
201 {
202     CHECK_OWRAP(self, "showingDesktop");
203     if (!PyArg_ParseTuple(args, ":showingDesktop"))
204         return NULL;
205     return PyInt_FromLong(!!screen_showing_desktop);
206 }
207
208 static PyObject *owrap_setShowingDesktop(OpenboxWrap *self, PyObject *args)
209 {
210     int show;
211
212     CHECK_OWRAP(self, "setShowingDesktop");
213     if (!PyArg_ParseTuple(args, "i:setShowingDesktop", &show))
214         return NULL;
215     screen_show_desktop(show);
216     Py_INCREF(Py_None);
217     return Py_None;
218 }
219
220 static PyObject *owrap_screenArea(OpenboxWrap *self, PyObject *args)
221 {
222     int desktop;
223     Rect *area;
224     PyObject *tuple;
225
226     CHECK_OWRAP(self, "screenArea");
227     if (!PyArg_ParseTuple(args, "i:screenArea", &desktop))
228         return NULL;
229
230     area = screen_area(desktop);
231     if (area == NULL) {
232         PyErr_SetString(PyExc_ValueError, "invalid desktop");
233         return NULL;
234     }
235
236     tuple = PyTuple_New(4);
237     PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(area->x));
238     PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(area->y));
239     PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(area->width));
240     PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(area->height));
241     return tuple;
242 }
243
244 static PyObject *owrap_screenStrut(OpenboxWrap *self, PyObject *args)
245 {
246     int desktop;
247     Strut *strut;
248     PyObject *tuple;
249
250     CHECK_OWRAP(self, "screenStrut");
251     if (!PyArg_ParseTuple(args, "i:screenStrut", &desktop))
252         return NULL;
253
254     strut = screen_strut(desktop);
255     if (strut == NULL) {
256         PyErr_SetString(PyExc_ValueError, "invalid desktop");
257         return NULL;
258     }
259
260     tuple = PyTuple_New(4);
261     PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(strut->left));
262     PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(strut->top));
263     PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(strut->right));
264     PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(strut->bottom));
265     return tuple;
266 }
267
268 static PyObject *owrap_physicalSize(OpenboxWrap *self, PyObject *args)
269 {
270     PyObject *tuple;
271
272     CHECK_OWRAP(self, "physicalSize");
273     if (!PyArg_ParseTuple(args, ":physicalSize"))
274         return NULL;
275
276     tuple = PyTuple_New(2);
277     PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(screen_physical_size.width));
278     PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(screen_physical_size.height));
279     return tuple;
280 }
281
282 static PyObject *owrap_screenNumber(OpenboxWrap *self, PyObject *args)
283 {
284     CHECK_OWRAP(self, "screenNumber");
285     if (!PyArg_ParseTuple(args, ":screenNumber"))
286         return NULL;
287     return PyInt_FromLong(ob_screen);
288 }
289
290 static PyObject *owrap_rootWindow(OpenboxWrap *self, PyObject *args)
291 {
292     CHECK_OWRAP(self, "rootWindow");
293     if (!PyArg_ParseTuple(args, ":rootWindow"))
294         return NULL;
295     return PyInt_FromLong(ob_root);
296 }
297
298 static PyObject *owrap_clientList(OpenboxWrap *self, PyObject *args)
299 {
300     CHECK_OWRAP(self, "clientList");
301     if (!PyArg_ParseTuple(args, ":clientList"))
302         return NULL;
303     Py_INCREF(self->client_list);
304     return self->client_list;
305 }
306
307 #define METH(n, d) {#n, (PyCFunction)owrap_##n, METH_VARARGS, #d}
308
309 static PyMethodDef OpenboxWrapMethods[] = {
310     METH(shutdown,
311          "Causes Openbox to shutdown and exit."),
312     METH(restart,
313          "Causes Openbox to shutdown and restart. If path is specified, "
314          "Openbox will shutdown and attempt to run the specified executable "
315          "instead of restarting itself. If that fails, however, it will "
316          "restart itself."),
317     METH(state,
318          "Returns Openbox's current state, this will be one of the State "
319          "constants."),
320     METH(desktop,
321          "Returns the number of the currently visible desktop. This will be "
322          "in the range of [0, numDesktops())."),
323     METH(setDesktop,
324          "Sets the specified desktop as the visible desktop."),
325     METH(setNextDesktop,
326          "Sets the visible desktop to the next desktop, optionally wrapping "
327          "around when reaching the last."),
328     METH(setPreviousDesktop,
329          "Sets the visible desktop to the previous desktop, optionally "
330          "wrapping around when reaching the first."),
331     METH(numDesktops,
332          "Returns the number of desktops available."),
333     METH(desktopNames,
334          "Returns a tuple of names, containing a name for each desktop. The "
335          "tuple may have a length greater than numDesktops() if more names "
336          "have been specified."),
337     METH(setDesktopNames,
338          "Sets the names for the desktops."),
339     METH(showingDesktop,
340          "Returns True or False, depicting if Openbox is in 'showing the "
341          "desktop' mode. In 'showing the desktop' mode, all normal clients "
342          "are hidden and the desktop is given focus if possible."),
343     METH(setShowingDesktop,
344          "Enters or leaves 'showing the desktop' mode. See showingDesktop() "
345          "for a description of this mode."),
346     METH(screenArea,
347          "Returns the on-screen available area. This is the area not reserved "
348          "by applications' struts. Windows should be placed within this area, "
349          "not within the physicalSize()."),
350     METH(screenStrut,
351          "Returns the combined strut which has been reserved by all "
352          "applications on the desktops."),
353     METH(physicalSize,
354          "Returns the physical size of the display device (in pixels)."),
355     METH(screenNumber,
356          "Returns the number of the screen on which Openbox is running."),
357     METH(rootWindow,
358          "Return the window id of the root window."),
359     METH(clientList,
360          "Returns a all clients currently being managed by Openbox. This list "
361          "is updated as clients are managed and closed/destroyed/released."),
362     { NULL, NULL, 0, NULL }
363 };
364
365 /***************************************************************************
366  
367    Type methods/struct
368  
369  ***************************************************************************/
370
371 /*static PyObject *owrap_getattr(OpenboxWrap *self, char *name)
372 {
373     CHECK_OWRAP(self, "getattr");
374     return Py_FindMethod(OpenboxWrapAttributeMethods, (PyObject*)self, name);
375 }*/
376
377 static void owrap_dealloc(OpenboxWrap *self)
378 {
379     PyObject_Del((PyObject*) self);
380 }
381
382 static PyTypeObject OpenboxWrapType = {
383     PyObject_HEAD_INIT(NULL)
384     0,
385     "Openbox",
386     sizeof(OpenboxWrap),
387     0,
388     (destructor) owrap_dealloc,     /*tp_dealloc*/
389     0,                              /*tp_print*/
390     0,                              /*tp_getattr*/
391     0,                              /*tp_setattr*/
392     0,                              /*tp_compare*/
393     0,                              /*tp_repr*/
394     0,                              /*tp_as_number*/
395     0,                              /*tp_as_sequence*/
396     0,                              /*tp_as_mapping*/
397     0,                              /*tp_hash */
398 };
399
400 /***************************************************************************
401  
402    Define the type 'OpenboxState'
403
404  ***************************************************************************/
405
406 #define IS_OSTATE(v)  ((v)->ob_type == &OpenboxStateType)
407 #define CHECK_OSTATE(self, funcname) { \
408     if (!IS_OSTATE(self)) { \
409         PyErr_SetString(PyExc_TypeError, \
410                         "descriptor '" funcname "' requires a 'State' " \
411                         "object"); \
412         return NULL; \
413     } \
414 }
415
416 staticforward PyTypeObject OpenboxStateType;
417
418 typedef struct OpenboxState {
419     PyObject_HEAD
420 } OpenboxState;
421
422 static void ostate_dealloc(PyObject *self)
423 {
424     PyObject_Del(self);
425 }
426
427 static PyObject *ostate_getattr(OpenboxState *self, char *name)
428 {
429     struct S { char *name; int val; };
430     struct S s[] = {
431         { "Starting", State_Starting },
432         { "Running", State_Running },
433         { "Exiting", State_Exiting },
434         { NULL, 0 } };
435     int i;
436
437     CHECK_OSTATE(self, "__getattr__");
438
439     for (i = 0; s[i].name != NULL; ++i)
440         if (!strcmp(s[i].name, name))
441             return PyInt_FromLong(s[i].val);
442     PyErr_SetString(PyExc_AttributeError, "invalid attribute");
443     return NULL;
444 }
445
446 static PyTypeObject OpenboxStateType = {
447     PyObject_HEAD_INIT(NULL)
448     0,
449     "State",
450     sizeof(OpenboxState),
451     0,
452     (destructor) ostate_dealloc,    /*tp_dealloc*/
453     0,                              /*tp_print*/
454     (getattrfunc) ostate_getattr,   /*tp_getattr*/
455     0,                              /*tp_setattr*/
456     0,                              /*tp_compare*/
457     0,                              /*tp_repr*/
458     0,                              /*tp_as_number*/
459     0,                              /*tp_as_sequence*/
460     0,                              /*tp_as_mapping*/
461     0,                              /*tp_hash */
462 };
463
464 /***************************************************************************
465  
466    External methods
467  
468  ***************************************************************************/
469
470 void openboxwrap_startup()
471 {
472     PyObject *ob, *obdict, *state;
473
474     OpenboxWrapType.ob_type = &PyType_Type;
475     OpenboxWrapType.tp_methods = OpenboxWrapMethods;
476     PyType_Ready(&OpenboxWrapType);
477
478     /* get the ob module/dict */
479     ob = PyImport_ImportModule("ob"); /* new */
480     g_assert(ob != NULL);
481     obdict = PyModule_GetDict(ob); /* borrowed */
482     g_assert(obdict != NULL);
483
484     /* add an Openbox instance to the ob module */
485     openboxwrap_obj = PyObject_New(OpenboxWrap, &OpenboxWrapType);
486     openboxwrap_obj->client_list = PyList_New(0);
487
488     PyDict_SetItemString(obdict, "Openbox", (PyObject*) openboxwrap_obj);
489
490     /* add an instance of OpenboxState */
491     state = (PyObject*) PyObject_New(OpenboxState, &OpenboxStateType);
492     PyDict_SetItemString(obdict, "State", state);
493     Py_DECREF(state);
494
495     Py_DECREF(ob);
496 }
497
498 void openboxwrap_shutdown()
499 {
500     Py_DECREF(openboxwrap_obj->client_list);
501     Py_DECREF(openboxwrap_obj);
502 }