1 // AppletWindow.cc --- Applet info Window
2 //
3 // Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Rob Caelers & Raymond Penners
4 // All rights reserved.
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 #include "preinclude.h"
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "nls.h"
26 #include "debug.hh"
27
28 #include "W32AppletWindow.hh"
29 #include "TimerBoxControl.hh"
30
31 #if defined(interface)
32 #undef interface
33 #endif
34
35 #include "Applet.hh"
36 #include "GUI.hh"
37 #include "Menus.hh"
38
W32AppletWindow()39 W32AppletWindow::W32AppletWindow()
40 {
41 TRACE_ENTER("W32AppletWindow::W32AppletWindow");
42
43 memset(&local_heartbeat_data, 0, sizeof(AppletHeartbeatData));
44 memset(&local_menu_data, 0, sizeof(AppletMenuData));
45 memset(&heartbeat_data, 0, sizeof(AppletHeartbeatData));
46 memset(&menu_data, 0, sizeof(AppletMenuData));
47
48 thread_id = 0;
49 thread_handle = NULL;
50 timer_box_view = this;
51 applet_window = NULL;
52 heartbeat_data.enabled = false;
53 local_applet_window = NULL;
54 init_menu(NULL);
55
56 ::InitializeCriticalSection(&heartbeat_data_lock);
57 thread_abort_event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
58 heartbeat_data_event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
59
60 // Intentionally last line, as this one calls W32AW::set_enabled(), e.g.
61 timer_box_control = new TimerBoxControl("applet", *this);
62
63 TRACE_EXIT();
64 }
65
~W32AppletWindow()66 W32AppletWindow::~W32AppletWindow()
67 {
68 TRACE_ENTER("W32AppletWindow::~W32AppletWindow");
69
70 /* before this instance is destroyed we signal and wait for its worker thread to terminate. this
71 isn't ideal because the gui will be blocked while we wait for termination if this destructor is
72 called from the main thread. current conditions are acceptable, however. 2/12/2012
73 */
74 heartbeat_data.enabled = false;
75 SetEvent( thread_abort_event );
76 if( thread_handle )
77 {
78 WaitForSingleObject( thread_handle, INFINITE );
79 CloseHandle( thread_handle );
80 }
81
82 if( thread_abort_event )
83 CloseHandle( thread_abort_event );
84
85 if( heartbeat_data_event )
86 CloseHandle( heartbeat_data_event );
87
88 DeleteCriticalSection(&heartbeat_data_lock);
89
90 delete timer_box_control;
91
92 TRACE_EXIT();
93 }
94
95
96 static HWND
RecursiveFindWindow(HWND hwnd,LPCTSTR lpClassName)97 RecursiveFindWindow(HWND hwnd, LPCTSTR lpClassName)
98 {
99 static char buf[80];
100 int num = GetClassName(hwnd, buf, sizeof(buf)-1);
101 buf[num] = 0;
102 HWND ret = NULL;
103
104 if (! stricmp(lpClassName, buf))
105 {
106 ret = hwnd;
107 }
108 else
109 {
110 HWND child = FindWindowEx(hwnd, 0, NULL, NULL);
111 while (child != NULL)
112 {
113 ret = RecursiveFindWindow(child, lpClassName);
114 if (ret)
115 {
116 break;
117 }
118 child = FindWindowEx(hwnd, child, NULL, NULL);
119 }
120 }
121 return ret;
122 }
123
124
125
126 void
set_slot(BreakId id,int slot)127 W32AppletWindow::set_slot(BreakId id, int slot)
128 {
129 TRACE_ENTER_MSG("W32AppletWindow::set_slot", int(id) << ", " << slot);
130 heartbeat_data.slots[slot] = (short) id;
131 TRACE_EXIT();
132 }
133
134 void
set_time_bar(BreakId id,std::string text,ITimeBar::ColorId primary_color,int primary_val,int primary_max,ITimeBar::ColorId secondary_color,int secondary_val,int secondary_max)135 W32AppletWindow::set_time_bar(BreakId id,
136 std::string text,
137 ITimeBar::ColorId primary_color,
138 int primary_val, int primary_max,
139 ITimeBar::ColorId secondary_color,
140 int secondary_val, int secondary_max)
141 {
142 TRACE_ENTER_MSG("W32AppletWindow::set_time_bar", int(id) << "=" << text);
143 strncpy(heartbeat_data.bar_text[id], text.c_str(), APPLET_BAR_TEXT_MAX_LENGTH-1);
144 heartbeat_data.bar_text[id][APPLET_BAR_TEXT_MAX_LENGTH-1] = '\0';
145 heartbeat_data.bar_primary_color[id] = primary_color;
146 heartbeat_data.bar_primary_val[id] = primary_val;
147 heartbeat_data.bar_primary_max[id] = primary_max;
148 heartbeat_data.bar_secondary_color[id] = secondary_color;
149 heartbeat_data.bar_secondary_val[id] = secondary_val;
150 heartbeat_data.bar_secondary_max[id] = secondary_max;
151 TRACE_EXIT();
152 }
153
154 void
update_view()155 W32AppletWindow::update_view()
156 {
157 TRACE_ENTER("W32AppletWindow::update_view");
158
159 BOOL entered = ::TryEnterCriticalSection(&heartbeat_data_lock);
160 if (entered)
161 {
162 memcpy(&local_heartbeat_data, &heartbeat_data, sizeof(AppletHeartbeatData));
163
164 update_applet_window();
165
166 if (!menu_sent)
167 {
168 memcpy(&local_menu_data, &menu_data, sizeof(AppletMenuData));
169 local_applet_window = applet_window;
170 menu_sent = true;
171 }
172
173 SetEvent(heartbeat_data_event);
174 ::LeaveCriticalSection(&heartbeat_data_lock);
175 }
176
177 TRACE_EXIT();
178 }
179
180 void
update_menu()181 W32AppletWindow::update_menu()
182 {
183 TRACE_ENTER("W32AppletWindow::update_menu");
184 if (local_applet_window != NULL)
185 {
186 TRACE_MSG("sending");
187
188 COPYDATASTRUCT msg;
189 msg.dwData = APPLET_MESSAGE_MENU;
190 msg.cbData = sizeof(AppletMenuData);
191 msg.lpData = &local_menu_data;
192 SendMessage(local_applet_window, WM_COPYDATA, 0, (LPARAM) &msg);
193 }
194 TRACE_EXIT();
195 }
196
197 void
update_time_bars()198 W32AppletWindow::update_time_bars()
199 {
200 TRACE_ENTER("W32AppletWindow::update_time_bars");
201 if (local_applet_window != NULL)
202 {
203 COPYDATASTRUCT msg;
204 msg.dwData = APPLET_MESSAGE_HEARTBEAT;
205 msg.cbData = sizeof(AppletHeartbeatData);
206 msg.lpData = &local_heartbeat_data;
207 TRACE_MSG("sending: enabled=" << local_heartbeat_data.enabled);
208 for (size_t i = 0; i < BREAK_ID_SIZEOF; i++)
209 {
210 TRACE_MSG("sending: slots[]=" << local_heartbeat_data.slots[i]);
211 }
212 SendMessage(local_applet_window, WM_COPYDATA, 0, (LPARAM) &msg);
213 }
214 TRACE_EXIT();
215 }
216
217 void
update_applet_window()218 W32AppletWindow::update_applet_window()
219 {
220 TRACE_ENTER("W32AppletWindow::get_applet_window");
221 HWND previous_applet_window = applet_window;
222 if (applet_window == NULL || !IsWindow(applet_window))
223 {
224 HWND taskbar = FindWindow("Shell_TrayWnd",NULL);
225 applet_window = RecursiveFindWindow(taskbar, APPLET_WINDOW_CLASS_NAME);
226 menu_sent = false;
227 }
228
229 if (previous_applet_window == NULL && applet_window != NULL)
230 {
231 state_changed_signal.emit(AppletWindow::APPLET_STATE_ACTIVE);
232 }
233 else if (previous_applet_window != NULL && applet_window == NULL)
234 {
235 state_changed_signal.emit(AppletWindow::APPLET_STATE_DISABLED);
236 }
237
238 TRACE_EXIT();
239 }
240
241
242 void
set_enabled(bool enabled)243 W32AppletWindow::set_enabled( bool enabled )
244 {
245 TRACE_ENTER_MSG( "W32AppletWindow::set_enabled", enabled );
246 DWORD thread_exit_code = 0;
247
248 heartbeat_data.enabled = enabled;
249
250 if( !enabled )
251 return;
252
253 if( thread_id
254 && thread_handle
255 && GetExitCodeThread( thread_handle, &thread_exit_code )
256 && ( thread_exit_code == STILL_ACTIVE )
257 )
258 return;
259
260 if( !thread_id )
261 {
262 // if there is no id but a handle then this instance's worker thread has exited or is exiting.
263 if( thread_handle )
264 CloseHandle( thread_handle );
265
266 thread_id = 0;
267 SetLastError( 0 );
268 thread_handle =
269 (HANDLE)_beginthreadex( NULL, 0, run_event_pipe_static, this, 0, (unsigned int *)&thread_id );
270
271 if( !thread_handle || !thread_id )
272 {
273 TRACE_MSG( "Thread could not be created. GetLastError : " << GetLastError() );
274 }
275 }
276
277 TRACE_EXIT();
278 }
279
280
281 unsigned __stdcall
run_event_pipe_static(void * param)282 W32AppletWindow::run_event_pipe_static( void *param )
283 {
284 W32AppletWindow *pThis = (W32AppletWindow *) param;
285 pThis->run_event_pipe();
286 // invalidate the id to signal the thread is exiting
287 pThis->thread_id = 0;
288 return (DWORD) 0;
289 }
290
291
292 void
run_event_pipe()293 W32AppletWindow::run_event_pipe()
294 {
295 const DWORD current_thread_id = GetCurrentThreadId();
296
297 TRACE_ENTER_MSG( "W32AppletWindow::run_event_pipe [ id: ", current_thread_id << " ]" );
298
299 while( thread_id == current_thread_id )
300 {
301 /* JS: thread_abort_event must be first in the array of events.
302 the index returned by WaitForMultipleObjectsEx() corresponds to the first
303 signaled event in the array if more than one is signaled
304 */
305 HANDLE events[ 2 ] = { thread_abort_event, heartbeat_data_event };
306 int const events_count = ( sizeof( events ) / sizeof( events[ 0 ] ) );
307
308 DWORD wait_result = WaitForMultipleObjectsEx( events_count, events, FALSE, INFINITE, FALSE );
309
310 if( ( wait_result == WAIT_FAILED ) || ( wait_result == ( WAIT_OBJECT_0 + 0 ) ) )
311 break;
312
313 if( heartbeat_data.enabled && ( wait_result == ( WAIT_OBJECT_0 + 1 ) ) )
314 {
315 EnterCriticalSection( &heartbeat_data_lock );
316
317 update_time_bars();
318 update_menu();
319
320 LeaveCriticalSection( &heartbeat_data_lock );
321 }
322 }
323
324 TRACE_EXIT();
325 }
326
327
328 void
init_menu(HWND hwnd)329 W32AppletWindow::init_menu(HWND hwnd)
330 {
331 menu_data.num_items = 0;
332 menu_sent = false;
333
334 /*
335 As noted in frontend/win32/applet/include/applet.hh:
336 We pass the command_window HWND as a LONG for compatibility.
337 */
338 menu_data.command_window = HandleToLong( hwnd );
339 }
340
341
342 void
add_menu(const char * text,short cmd,int flags)343 W32AppletWindow::add_menu(const char *text, short cmd, int flags)
344 {
345 AppletMenuItemData *d = &menu_data.items[menu_data.num_items++];
346 d->command = cmd;
347 strcpy(d->text, text);
348 d->flags = flags;
349 }
350
351
352 AppletWindow::AppletState
activate_applet()353 W32AppletWindow::activate_applet()
354 {
355 update_applet_window();
356 return applet_window != NULL ? APPLET_STATE_ACTIVE : APPLET_STATE_DISABLED;
357 }
358
359
360 void
deactivate_applet()361 W32AppletWindow::deactivate_applet()
362 {
363 }
364
365
366 void
set_geometry(Orientation orientation,int size)367 W32AppletWindow::set_geometry(Orientation orientation, int size)
368 {
369 (void) orientation;
370 (void) size;
371 }
372
373
374 bool
on_applet_command(int command)375 W32AppletWindow::on_applet_command(int command)
376 {
377 TRACE_ENTER_MSG("W32AppletWindow::on_applet_command", command);
378 IGUI *gui = GUI::get_instance();
379 Menus *menus = gui->get_menus();
380 menus->applet_command(command);
381 TRACE_EXIT();
382 return false;
383 }
384
385
386 GdkFilterReturn
win32_filter_func(void * xevent,GdkEvent * event)387 W32AppletWindow::win32_filter_func (void *xevent,
388 GdkEvent *event)
389 {
390 (void) event;
391 MSG *msg = (MSG *) xevent;
392 GdkFilterReturn ret = GDK_FILTER_CONTINUE;
393
394 switch (msg->message)
395 {
396 case WM_USER:
397 {
398 sigc::slot<bool> my_slot = sigc::bind(sigc::mem_fun(*this, &W32AppletWindow::on_applet_command),
399 (int) msg->wParam);
400 Glib::signal_idle().connect(my_slot);
401
402 ret = GDK_FILTER_REMOVE;
403 }
404 break;
405
406 case WM_USER + 1:
407 {
408 timer_box_control->force_cycle();
409 ret = GDK_FILTER_REMOVE;
410 }
411 break;
412 }
413 return ret;
414 }
415