1 /*
2  * Copyright (c) 2010 Mike Massonnet, <mmassonnet@xfce.org>
3  * Copyright (c) 2018 Rozhuk Ivan <rozhuk.im@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  */
10 
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14 
15 #include <stdlib.h>
16 #include <glib-object.h>
17 #include <gtk/gtk.h>
18 #define WNCK_I_KNOW_THIS_IS_UNSTABLE
19 #include <libwnck/libwnck.h>
20 
21 #include "app-manager.h"
22 #include "task-manager.h"
23 
24 
25 
26 typedef struct _XtmAppManagerClass XtmAppManagerClass;
27 struct _XtmAppManagerClass
28 {
29 	GObjectClass		parent_class;
30 };
31 struct _XtmAppManager
32 {
33 	GObject			parent;
34 	/*<private>*/
35 	GArray *		apps;
36 };
37 G_DEFINE_TYPE (XtmAppManager, xtm_app_manager, G_TYPE_OBJECT)
38 
39 static void	xtm_app_manager_finalize			(GObject *object);
40 
41 static GPid	app_get_pid					(WnckApplication *application);
42 static gint	app_pid_compare_fn				(gconstpointer a, gconstpointer b);
43 
44 static void	apps_add_application				(GArray *apps, WnckApplication *application, GPid pid);
45 static void	apps_remove_application				(GArray *apps, WnckApplication *application);
46 static App *	apps_lookup_pid					(GArray *apps, GPid pid);
47 static App *	apps_lookup_app					(GArray *apps, WnckApplication *application);
48 static void	application_opened				(WnckScreen *screen, WnckApplication *application, XtmAppManager *manager);
49 static void	application_closed				(WnckScreen *screen, WnckApplication *application, XtmAppManager *manager);
50 
51 
52 
53 static void
xtm_app_manager_class_init(XtmAppManagerClass * klass)54 xtm_app_manager_class_init (XtmAppManagerClass *klass)
55 {
56 	GObjectClass *class = G_OBJECT_CLASS (klass);
57 	xtm_app_manager_parent_class = g_type_class_peek_parent (klass);
58 	class->finalize = xtm_app_manager_finalize;
59 }
60 
61 static void
xtm_app_manager_init(XtmAppManager * manager)62 xtm_app_manager_init (XtmAppManager *manager)
63 {
64 	WnckApplication *application;
65 	WnckScreen *screen = wnck_screen_get_default ();
66 	GList *windows, *l;
67 
68 	/* Retrieve initial applications */
69 	while (gtk_events_pending ())
70 		gtk_main_iteration ();
71 
72 	manager->apps = g_array_new (FALSE, FALSE, sizeof (App));
73 	windows = wnck_screen_get_windows (screen);
74 	for (l = windows; l != NULL; l = l->next)
75 	{
76 		WnckWindow *window = WNCK_WINDOW (l->data);
77 		if (wnck_window_get_window_type (window) != WNCK_WINDOW_NORMAL)
78 			continue;
79 
80 		application = wnck_window_get_application (window);
81 		apps_add_application (manager->apps, application, app_get_pid (application));
82 	}
83 
84 	G_DEBUG_FMT ("Initial applications: %d", manager->apps->len);
85 
86 	/* Connect signals */
87 	g_signal_connect (screen, "application-opened", G_CALLBACK (application_opened), manager);
88 	g_signal_connect (screen, "application-closed", G_CALLBACK (application_closed), manager);
89 }
90 
91 static void
xtm_app_manager_finalize(GObject * object)92 xtm_app_manager_finalize (GObject *object)
93 {
94 	g_array_free (XTM_APP_MANAGER (object)->apps, TRUE);
95 }
96 
97 static GPid
app_get_pid(WnckApplication * application)98 app_get_pid(WnckApplication *application)
99 {
100 	GPid pid;
101 	GList *windows;
102 
103 	if (NULL == application)
104 		return (0);
105 	pid = wnck_application_get_pid (application);
106 	if (pid != 0)
107 		return (pid);
108 	windows = wnck_application_get_windows (application);
109 	if (NULL != windows && NULL != windows->data)
110 		return (wnck_window_get_pid (WNCK_WINDOW (windows->data)));
111 	return (0);
112 }
113 
114 static gint
app_pid_compare_fn(gconstpointer a,gconstpointer b)115 app_pid_compare_fn(gconstpointer a, gconstpointer b)
116 {
117 	return (((const App*)a)->pid - ((const App*)b)->pid);
118 }
119 
120 static void
apps_add_application(GArray * apps,WnckApplication * application,GPid pid)121 apps_add_application (GArray *apps, WnckApplication *application, GPid pid)
122 {
123 	App app;
124 
125 	if (apps_lookup_pid (apps, pid))
126 		return;
127 
128 	app.application = application;
129 	app.pid = pid;
130 	g_snprintf (app.name, sizeof(app.name), "%s", wnck_application_get_name (application));
131 	app.icon = wnck_application_get_mini_icon (application);
132 	g_object_ref (app.icon);
133 
134 	g_array_append_val (apps, app);
135 	g_array_sort (apps, app_pid_compare_fn);
136 }
137 
138 static void
apps_remove_application(GArray * apps,WnckApplication * application)139 apps_remove_application (GArray *apps, WnckApplication *application)
140 {
141 	App *app = apps_lookup_pid(apps, app_get_pid (application));
142 
143 	if (app == NULL)
144 		app = apps_lookup_app(apps, application);
145 	if (app == NULL)
146 		return;
147 	g_object_unref (app->icon);
148 	g_array_remove_index (apps, (guint)(((size_t)app - (size_t)apps->data) / sizeof(App)));
149 }
150 
151 static App *
apps_lookup_pid(GArray * apps,GPid pid)152 apps_lookup_pid (GArray *apps, GPid pid)
153 {
154 	App tapp;
155 
156 	tapp.pid = pid;
157 
158 	return (bsearch(&tapp, apps->data, apps->len, sizeof(App), app_pid_compare_fn));
159 }
160 
161 static App *
apps_lookup_app(GArray * apps,WnckApplication * application)162 apps_lookup_app (GArray *apps, WnckApplication *application)
163 {
164 	App *tapp;
165 	guint i;
166 
167 	for (i = 0; i < apps->len; i++) {
168 		tapp = &g_array_index (apps, App, i);
169 		if (tapp->application == application)
170 			return (tapp);
171 	}
172 
173 	return (NULL);
174 }
175 
176 static void
application_opened(WnckScreen * screen __unused,WnckApplication * application,XtmAppManager * manager)177 application_opened (WnckScreen *screen __unused, WnckApplication *application, XtmAppManager *manager)
178 {
179 	GPid pid = app_get_pid (application);
180 	G_DEBUG_FMT ("Application opened %p %d", (void*)application, pid);
181 	apps_add_application (manager->apps, application, pid);
182 }
183 
184 static void
application_closed(WnckScreen * screen __unused,WnckApplication * application,XtmAppManager * manager)185 application_closed (WnckScreen *screen __unused, WnckApplication *application, XtmAppManager *manager)
186 {
187 	G_DEBUG_FMT ("Application closed %p", (void*)application);
188 	apps_remove_application (manager->apps, application);
189 }
190 
191 
192 
193 XtmAppManager *
xtm_app_manager_new(void)194 xtm_app_manager_new (void)
195 {
196 	return g_object_new (XTM_TYPE_APP_MANAGER, NULL);
197 }
198 
199 App *
xtm_app_manager_get_app_from_pid(XtmAppManager * manager,GPid pid)200 xtm_app_manager_get_app_from_pid (XtmAppManager *manager, GPid pid)
201 {
202 	return apps_lookup_pid (manager->apps, pid);
203 }
204