| |
1 /* |
| |
2 * gaim |
| |
3 * |
| |
4 * File: gtkwin32dep.c |
| |
5 * Date: June, 2002 |
| |
6 * Description: Windows dependant code for Gaim |
| |
7 * |
| |
8 * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com> |
| |
9 * |
| |
10 * This program is free software; you can redistribute it and/or modify |
| |
11 * it under the terms of the GNU General Public License as published by |
| |
12 * the Free Software Foundation; either version 2 of the License, or |
| |
13 * (at your option) any later version. |
| |
14 * |
| |
15 * This program is distributed in the hope that it will be useful, |
| |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
18 * GNU General Public License for more details. |
| |
19 * |
| |
20 * You should have received a copy of the GNU General Public License |
| |
21 * along with this program; if not, write to the Free Software |
| |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
23 * |
| |
24 */ |
| |
25 #define _WIN32_IE 0x500 |
| |
26 #ifndef WINVER |
| |
27 #define WINVER 0x0500 /* W2K */ |
| |
28 #endif |
| |
29 #include <windows.h> |
| |
30 #include <io.h> |
| |
31 #include <stdlib.h> |
| |
32 #include <stdio.h> |
| |
33 #include <winuser.h> |
| |
34 |
| |
35 #include <glib.h> |
| |
36 #include <glib/gstdio.h> |
| |
37 #include <gtk/gtk.h> |
| |
38 #include <gdk/gdkwin32.h> |
| |
39 |
| |
40 #include "debug.h" |
| |
41 #include "notify.h" |
| |
42 |
| |
43 #include "resource.h" |
| |
44 #include "idletrack.h" |
| |
45 #include "zlib.h" |
| |
46 #include "untar.h" |
| |
47 |
| |
48 #include <libintl.h> |
| |
49 |
| |
50 #include "gtkwin32dep.h" |
| |
51 #include "win32dep.h" |
| |
52 #include "gtkconv.h" |
| |
53 #include "wspell.h" |
| |
54 |
| |
55 /* |
| |
56 * GLOBALS |
| |
57 */ |
| |
58 HINSTANCE gaimexe_hInstance = 0; |
| |
59 HINSTANCE gtkgaimdll_hInstance = 0; |
| |
60 HWND messagewin_hwnd; |
| |
61 static int gtkwin32_handle; |
| |
62 |
| |
63 typedef BOOL (CALLBACK* LPFNFLASHWINDOWEX)(PFLASHWINFO); |
| |
64 static LPFNFLASHWINDOWEX MyFlashWindowEx = NULL; |
| |
65 |
| |
66 |
| |
67 /* |
| |
68 * PUBLIC CODE |
| |
69 */ |
| |
70 |
| |
71 HINSTANCE gtkwgaim_hinstance(void) { |
| |
72 return gaimexe_hInstance; |
| |
73 } |
| |
74 |
| |
75 int gtkwgaim_gz_decompress(const char* in, const char* out) { |
| |
76 gzFile fin; |
| |
77 FILE *fout; |
| |
78 char buf[1024]; |
| |
79 int ret; |
| |
80 |
| |
81 if((fin = gzopen(in, "rb"))) { |
| |
82 if(!(fout = g_fopen(out, "wb"))) { |
| |
83 gaim_debug_error("gtkwgaim_gz_decompress", "Error opening file: %s\n", out); |
| |
84 gzclose(fin); |
| |
85 return 0; |
| |
86 } |
| |
87 } |
| |
88 else { |
| |
89 gaim_debug_error("gtkwgaim_gz_decompress", "gzopen failed to open: %s\n", in); |
| |
90 return 0; |
| |
91 } |
| |
92 |
| |
93 while((ret = gzread(fin, buf, 1024))) { |
| |
94 if(fwrite(buf, 1, ret, fout) < ret) { |
| |
95 gaim_debug_error("wgaim_gz_decompress", "Error writing %d bytes to file\n", ret); |
| |
96 gzclose(fin); |
| |
97 fclose(fout); |
| |
98 return 0; |
| |
99 } |
| |
100 } |
| |
101 fclose(fout); |
| |
102 gzclose(fin); |
| |
103 |
| |
104 if(ret < 0) { |
| |
105 gaim_debug_error("gtkwgaim_gz_decompress", "gzread failed while reading: %s\n", in); |
| |
106 return 0; |
| |
107 } |
| |
108 |
| |
109 return 1; |
| |
110 } |
| |
111 |
| |
112 int gtkwgaim_gz_untar(const char* filename, const char* destdir) { |
| |
113 char tmpfile[_MAX_PATH]; |
| |
114 char template[]="wgaimXXXXXX"; |
| |
115 |
| |
116 sprintf(tmpfile, "%s%s%s", g_get_tmp_dir(), G_DIR_SEPARATOR_S, _mktemp(template)); |
| |
117 if(gtkwgaim_gz_decompress(filename, tmpfile)) { |
| |
118 int ret; |
| |
119 if(untar(tmpfile, destdir, UNTAR_FORCE | UNTAR_QUIET)) |
| |
120 ret = 1; |
| |
121 else { |
| |
122 gaim_debug_error("gtkwgaim_gz_untar", "Failure untarring %s\n", tmpfile); |
| |
123 ret = 0; |
| |
124 } |
| |
125 g_unlink(tmpfile); |
| |
126 return ret; |
| |
127 } |
| |
128 else { |
| |
129 gaim_debug_error("gtkwgaim_gz_untar", "Failed to gz decompress %s\n", filename); |
| |
130 return 0; |
| |
131 } |
| |
132 } |
| |
133 |
| |
134 void gtkwgaim_shell_execute(const char *target, const char *verb, const char *clazz) { |
| |
135 |
| |
136 g_return_if_fail(target != NULL); |
| |
137 g_return_if_fail(verb != NULL); |
| |
138 |
| |
139 if (G_WIN32_HAVE_WIDECHAR_API()) { |
| |
140 SHELLEXECUTEINFOW wsinfo; |
| |
141 wchar_t *w_uri, *w_verb, *w_clazz = NULL; |
| |
142 |
| |
143 w_uri = g_utf8_to_utf16(target, -1, NULL, NULL, NULL); |
| |
144 w_verb = g_utf8_to_utf16(verb, -1, NULL, NULL, NULL); |
| |
145 |
| |
146 memset(&wsinfo, 0, sizeof(wsinfo)); |
| |
147 wsinfo.cbSize = sizeof(wsinfo); |
| |
148 wsinfo.lpVerb = w_verb; |
| |
149 wsinfo.lpFile = w_uri; |
| |
150 wsinfo.nShow = SW_SHOWNORMAL; |
| |
151 if (clazz != NULL) { |
| |
152 w_clazz = g_utf8_to_utf16(clazz, -1, NULL, NULL, NULL); |
| |
153 wsinfo.fMask |= SEE_MASK_CLASSNAME; |
| |
154 wsinfo.lpClass = w_clazz; |
| |
155 } |
| |
156 |
| |
157 if(!ShellExecuteExW(&wsinfo)) |
| |
158 gaim_debug_error("gtkwgaim", "Error opening URI: %s error: %d\n", |
| |
159 target, (int) wsinfo.hInstApp); |
| |
160 |
| |
161 g_free(w_uri); |
| |
162 g_free(w_verb); |
| |
163 g_free(w_clazz); |
| |
164 } else { |
| |
165 SHELLEXECUTEINFOA sinfo; |
| |
166 gchar *locale_uri; |
| |
167 |
| |
168 locale_uri = g_locale_from_utf8(target, -1, NULL, NULL, NULL); |
| |
169 |
| |
170 memset(&sinfo, 0, sizeof(sinfo)); |
| |
171 sinfo.cbSize = sizeof(sinfo); |
| |
172 sinfo.lpVerb = verb; |
| |
173 sinfo.lpFile = locale_uri; |
| |
174 sinfo.nShow = SW_SHOWNORMAL; |
| |
175 if (clazz != NULL) { |
| |
176 sinfo.fMask |= SEE_MASK_CLASSNAME; |
| |
177 sinfo.lpClass = clazz; |
| |
178 } |
| |
179 |
| |
180 if(!ShellExecuteExA(&sinfo)) |
| |
181 gaim_debug_error("gtkwgaim", "Error opening URI: %s error: %d\n", |
| |
182 target, (int) sinfo.hInstApp); |
| |
183 |
| |
184 g_free(locale_uri); |
| |
185 } |
| |
186 |
| |
187 } |
| |
188 |
| |
189 void gtkwgaim_notify_uri(const char *uri) { |
| |
190 /* We'll allow whatever URI schemes are supported by the |
| |
191 * default http browser. |
| |
192 */ |
| |
193 gtkwgaim_shell_execute(uri, "open", "http"); |
| |
194 } |
| |
195 |
| |
196 #define WM_FOCUS_REQUEST (WM_APP + 13) |
| |
197 |
| |
198 static LRESULT CALLBACK message_window_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { |
| |
199 |
| |
200 if (msg == WM_FOCUS_REQUEST) { |
| |
201 gaim_debug_info("gtkwgaim", "Got external Buddy List focus request."); |
| |
202 gaim_blist_set_visible(TRUE); |
| |
203 return TRUE; |
| |
204 } |
| |
205 |
| |
206 return DefWindowProc(hwnd, msg, wparam, lparam); |
| |
207 } |
| |
208 |
| |
209 static HWND wgaim_message_window_init(void) { |
| |
210 HWND win_hwnd; |
| |
211 WNDCLASSEX wcx; |
| |
212 LPCTSTR wname; |
| |
213 |
| |
214 wname = TEXT("WingaimMsgWinCls"); |
| |
215 |
| |
216 wcx.cbSize = sizeof(wcx); |
| |
217 wcx.style = 0; |
| |
218 wcx.lpfnWndProc = message_window_handler; |
| |
219 wcx.cbClsExtra = 0; |
| |
220 wcx.cbWndExtra = 0; |
| |
221 wcx.hInstance = gtkwgaim_hinstance(); |
| |
222 wcx.hIcon = NULL; |
| |
223 wcx.hCursor = NULL; |
| |
224 wcx.hbrBackground = NULL; |
| |
225 wcx.lpszMenuName = NULL; |
| |
226 wcx.lpszClassName = wname; |
| |
227 wcx.hIconSm = NULL; |
| |
228 |
| |
229 RegisterClassEx(&wcx); |
| |
230 |
| |
231 /* Create the window */ |
| |
232 if(!(win_hwnd = CreateWindow(wname, TEXT("WingaimMsgWin"), 0, 0, 0, 0, 0, |
| |
233 NULL, NULL, gtkwgaim_hinstance(), 0))) { |
| |
234 gaim_debug_error("gtkwgaim", |
| |
235 "Unable to create message window.\n"); |
| |
236 return NULL; |
| |
237 } |
| |
238 |
| |
239 return win_hwnd; |
| |
240 } |
| |
241 |
| |
242 static gboolean stop_flashing(GtkWidget *widget, GdkEventFocus *event, gpointer data) { |
| |
243 GtkWindow *window = data; |
| |
244 gtkwgaim_window_flash(window, FALSE); |
| |
245 return FALSE; |
| |
246 } |
| |
247 |
| |
248 void |
| |
249 gtkwgaim_window_flash(GtkWindow *window, gboolean flash) { |
| |
250 GdkWindow * gdkwin; |
| |
251 |
| |
252 g_return_if_fail(window != NULL); |
| |
253 |
| |
254 gdkwin = GTK_WIDGET(window)->window; |
| |
255 |
| |
256 g_return_if_fail(GDK_IS_WINDOW(gdkwin)); |
| |
257 g_return_if_fail(GDK_WINDOW_TYPE(gdkwin) != GDK_WINDOW_CHILD); |
| |
258 |
| |
259 if(GDK_WINDOW_DESTROYED(gdkwin)) |
| |
260 return; |
| |
261 |
| |
262 if(MyFlashWindowEx) { |
| |
263 FLASHWINFO info; |
| |
264 |
| |
265 memset(&info, 0, sizeof(FLASHWINFO)); |
| |
266 info.cbSize = sizeof(FLASHWINFO); |
| |
267 info.hwnd = GDK_WINDOW_HWND(gdkwin); |
| |
268 if (flash) |
| |
269 info.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG; |
| |
270 else |
| |
271 info.dwFlags = FLASHW_STOP; |
| |
272 info.dwTimeout = 0; |
| |
273 info.dwTimeout = 0; |
| |
274 |
| |
275 MyFlashWindowEx(&info); |
| |
276 } else |
| |
277 FlashWindow(GDK_WINDOW_HWND(gdkwin), flash); |
| |
278 } |
| |
279 |
| |
280 void |
| |
281 gtkwgaim_conv_blink(GaimConversation *conv, GaimMessageFlags flags) { |
| |
282 GaimGtkWindow *win; |
| |
283 GtkWindow *window; |
| |
284 |
| |
285 /* Don't flash for our own messages or system messages */ |
| |
286 if(flags & GAIM_MESSAGE_SEND || flags & GAIM_MESSAGE_SYSTEM) |
| |
287 return; |
| |
288 |
| |
289 if(conv == NULL) { |
| |
290 gaim_debug_info("gtkwgaim", "No conversation found to blink.\n"); |
| |
291 return; |
| |
292 } |
| |
293 |
| |
294 win = gaim_gtkconv_get_window(GAIM_GTK_CONVERSATION(conv)); |
| |
295 if(win == NULL) { |
| |
296 gaim_debug_info("gtkwgaim", "No conversation windows found to blink.\n"); |
| |
297 return; |
| |
298 } |
| |
299 window = GTK_WINDOW(win->window); |
| |
300 |
| |
301 gtkwgaim_window_flash(window, TRUE); |
| |
302 /* Stop flashing when window receives focus */ |
| |
303 g_signal_connect(G_OBJECT(window), "focus-in-event", |
| |
304 G_CALLBACK(stop_flashing), window); |
| |
305 } |
| |
306 |
| |
307 static gboolean |
| |
308 gtkwgaim_conv_im_blink(GaimAccount *account, const char *who, char **message, |
| |
309 GaimConversation *conv, GaimMessageFlags flags, void *data) |
| |
310 { |
| |
311 if (gaim_prefs_get_bool("/gaim/gtk/win32/blink_im")) |
| |
312 gtkwgaim_conv_blink(conv, flags); |
| |
313 return FALSE; |
| |
314 } |
| |
315 |
| |
316 void gtkwgaim_init(HINSTANCE hint) { |
| |
317 |
| |
318 gaim_debug_info("gtkwgaim", "gtkwgaim_init start\n"); |
| |
319 |
| |
320 gaimexe_hInstance = hint; |
| |
321 |
| |
322 /* IdleTracker Initialization */ |
| |
323 if(!wgaim_set_idlehooks()) |
| |
324 gaim_debug_error("gtkwgaim", "Failed to initialize idle tracker\n"); |
| |
325 |
| |
326 wgaim_gtkspell_init(); |
| |
327 gaim_debug_info("gtkwgaim", "GTK+ :%u.%u.%u\n", |
| |
328 gtk_major_version, gtk_minor_version, gtk_micro_version); |
| |
329 |
| |
330 messagewin_hwnd = wgaim_message_window_init(); |
| |
331 |
| |
332 MyFlashWindowEx = (LPFNFLASHWINDOWEX) wgaim_find_and_loadproc("user32.dll", "FlashWindowEx"); |
| |
333 |
| |
334 gaim_debug_info("gtkwgaim", "gtkwgaim_init end\n"); |
| |
335 } |
| |
336 |
| |
337 void gtkwgaim_post_init(void) { |
| |
338 |
| |
339 gaim_prefs_add_none("/gaim/gtk/win32"); |
| |
340 gaim_prefs_add_bool("/gaim/gtk/win32/blink_im", TRUE); |
| |
341 |
| |
342 gaim_signal_connect(gaim_gtk_conversations_get_handle(), |
| |
343 "displaying-im-msg", >kwin32_handle, GAIM_CALLBACK(gtkwgaim_conv_im_blink), |
| |
344 NULL); |
| |
345 |
| |
346 } |
| |
347 |
| |
348 /* Windows Cleanup */ |
| |
349 |
| |
350 void gtkwgaim_cleanup(void) { |
| |
351 gaim_debug_info("gtkwgaim", "gtkwgaim_cleanup\n"); |
| |
352 |
| |
353 if(messagewin_hwnd) |
| |
354 DestroyWindow(messagewin_hwnd); |
| |
355 |
| |
356 /* Idle tracker cleanup */ |
| |
357 wgaim_remove_idlehooks(); |
| |
358 |
| |
359 } |
| |
360 |
| |
361 /* DLL initializer */ |
| |
362 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { |
| |
363 gtkgaimdll_hInstance = hinstDLL; |
| |
364 return TRUE; |
| |
365 } |
| |
366 |
| |
367 typedef HMONITOR WINAPI gaim_MonitorFromWindow(HWND, DWORD); |
| |
368 typedef BOOL WINAPI gaim_GetMonitorInfo(HMONITOR, LPMONITORINFO); |
| |
369 |
| |
370 static gboolean |
| |
371 get_WorkingAreaRectForWindow(HWND hwnd, RECT *workingAreaRc) { |
| |
372 static gaim_MonitorFromWindow *the_MonitorFromWindow; |
| |
373 static gaim_GetMonitorInfo *the_GetMonitorInfo; |
| |
374 static gboolean initialized = FALSE; |
| |
375 |
| |
376 HMONITOR monitor; |
| |
377 MONITORINFO info; |
| |
378 |
| |
379 if(!initialized) { |
| |
380 the_MonitorFromWindow = (gaim_MonitorFromWindow*) |
| |
381 wgaim_find_and_loadproc("user32", "MonitorFromWindow"); |
| |
382 the_GetMonitorInfo = (gaim_GetMonitorInfo*) |
| |
383 wgaim_find_and_loadproc("user32", "GetMonitorInfoA"); |
| |
384 initialized = TRUE; |
| |
385 } |
| |
386 |
| |
387 if(!the_MonitorFromWindow) |
| |
388 return FALSE; |
| |
389 |
| |
390 if(!the_GetMonitorInfo) |
| |
391 return FALSE; |
| |
392 |
| |
393 monitor = the_MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); |
| |
394 |
| |
395 info.cbSize = sizeof(info); |
| |
396 if(!the_GetMonitorInfo(monitor, &info)) |
| |
397 return FALSE; |
| |
398 |
| |
399 CopyRect(workingAreaRc, &(info.rcWork)); |
| |
400 return TRUE; |
| |
401 } |
| |
402 |
| |
403 void gtkwgaim_ensure_onscreen(GtkWidget *win) { |
| |
404 RECT windowRect, workingAreaRect, intersectionRect; |
| |
405 HWND hwnd = GDK_WINDOW_HWND(win->window); |
| |
406 |
| |
407 g_return_if_fail(hwnd != NULL); |
| |
408 GetWindowRect(hwnd, &windowRect); |
| |
409 |
| |
410 gaim_debug_info("win32placement", |
| |
411 "Window RECT: L:%ld R:%ld T:%ld B:%ld\n", |
| |
412 windowRect.left, windowRect.right, |
| |
413 windowRect.top, windowRect.bottom); |
| |
414 |
| |
415 if(!get_WorkingAreaRectForWindow(hwnd, &workingAreaRect)) { |
| |
416 gaim_debug_info("win32placement", |
| |
417 "Couldn't get multimonitor working area\n"); |
| |
418 if(!SystemParametersInfo(SPI_GETWORKAREA, 0, &workingAreaRect, FALSE)) { |
| |
419 /* I don't think this will ever happen */ |
| |
420 workingAreaRect.left = 0; |
| |
421 workingAreaRect.top = 0; |
| |
422 workingAreaRect.bottom = GetSystemMetrics(SM_CYSCREEN); |
| |
423 workingAreaRect.right = GetSystemMetrics(SM_CXSCREEN); |
| |
424 } |
| |
425 } |
| |
426 |
| |
427 gaim_debug_info("win32placement", |
| |
428 "Working Area RECT: L:%ld R:%ld T:%ld B:%ld\n", |
| |
429 workingAreaRect.left, workingAreaRect.right, |
| |
430 workingAreaRect.top, workingAreaRect.bottom); |
| |
431 |
| |
432 /** If the conversation window doesn't intersect perfectly with the working area, |
| |
433 * move it to the top left corner of the working area */ |
| |
434 if(!(IntersectRect(&intersectionRect, &windowRect, &workingAreaRect) |
| |
435 && EqualRect(&intersectionRect, &windowRect))) { |
| |
436 gaim_debug_info("win32placement", |
| |
437 "conversation window out of working area, relocating\n"); |
| |
438 MoveWindow(hwnd, workingAreaRect.left, workingAreaRect.top, |
| |
439 (windowRect.right - windowRect.left), |
| |
440 (windowRect.bottom - windowRect.top), TRUE); |
| |
441 } |
| |
442 } |
| |
443 |