--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gtk/win32/gtkwin32dep.c Sun Apr 15 02:10:37 2007 +0000 @@ -0,0 +1,443 @@ +/* + * gaim + * + * File: gtkwin32dep.c + * Date: June, 2002 + * Description: Windows dependant code for Gaim + * + * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define _WIN32_IE 0x500 +#ifndef WINVER +#define WINVER 0x0500 /* W2K */ +#endif +#include <windows.h> +#include <io.h> +#include <stdlib.h> +#include <stdio.h> +#include <winuser.h> + +#include <glib.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> +#include <gdk/gdkwin32.h> + +#include "debug.h" +#include "notify.h" + +#include "resource.h" +#include "idletrack.h" +#include "zlib.h" +#include "untar.h" + +#include <libintl.h> + +#include "gtkwin32dep.h" +#include "win32dep.h" +#include "gtkconv.h" +#include "wspell.h" + +/* + * GLOBALS + */ +HINSTANCE gaimexe_hInstance = 0; +HINSTANCE gtkgaimdll_hInstance = 0; +HWND messagewin_hwnd; +static int gtkwin32_handle; + +typedef BOOL (CALLBACK* LPFNFLASHWINDOWEX)(PFLASHWINFO); +static LPFNFLASHWINDOWEX MyFlashWindowEx = NULL; + + +/* + * PUBLIC CODE + */ + +HINSTANCE gtkwgaim_hinstance(void) { + return gaimexe_hInstance; +} + +int gtkwgaim_gz_decompress(const char* in, const char* out) { + gzFile fin; + FILE *fout; + char buf[1024]; + int ret; + + if((fin = gzopen(in, "rb"))) { + if(!(fout = g_fopen(out, "wb"))) { + gaim_debug_error("gtkwgaim_gz_decompress", "Error opening file: %s\n", out); + gzclose(fin); + return 0; + } + } + else { + gaim_debug_error("gtkwgaim_gz_decompress", "gzopen failed to open: %s\n", in); + return 0; + } + + while((ret = gzread(fin, buf, 1024))) { + if(fwrite(buf, 1, ret, fout) < ret) { + gaim_debug_error("wgaim_gz_decompress", "Error writing %d bytes to file\n", ret); + gzclose(fin); + fclose(fout); + return 0; + } + } + fclose(fout); + gzclose(fin); + + if(ret < 0) { + gaim_debug_error("gtkwgaim_gz_decompress", "gzread failed while reading: %s\n", in); + return 0; + } + + return 1; +} + +int gtkwgaim_gz_untar(const char* filename, const char* destdir) { + char tmpfile[_MAX_PATH]; + char template[]="wgaimXXXXXX"; + + sprintf(tmpfile, "%s%s%s", g_get_tmp_dir(), G_DIR_SEPARATOR_S, _mktemp(template)); + if(gtkwgaim_gz_decompress(filename, tmpfile)) { + int ret; + if(untar(tmpfile, destdir, UNTAR_FORCE | UNTAR_QUIET)) + ret = 1; + else { + gaim_debug_error("gtkwgaim_gz_untar", "Failure untarring %s\n", tmpfile); + ret = 0; + } + g_unlink(tmpfile); + return ret; + } + else { + gaim_debug_error("gtkwgaim_gz_untar", "Failed to gz decompress %s\n", filename); + return 0; + } +} + +void gtkwgaim_shell_execute(const char *target, const char *verb, const char *clazz) { + + g_return_if_fail(target != NULL); + g_return_if_fail(verb != NULL); + + if (G_WIN32_HAVE_WIDECHAR_API()) { + SHELLEXECUTEINFOW wsinfo; + wchar_t *w_uri, *w_verb, *w_clazz = NULL; + + w_uri = g_utf8_to_utf16(target, -1, NULL, NULL, NULL); + w_verb = g_utf8_to_utf16(verb, -1, NULL, NULL, NULL); + + memset(&wsinfo, 0, sizeof(wsinfo)); + wsinfo.cbSize = sizeof(wsinfo); + wsinfo.lpVerb = w_verb; + wsinfo.lpFile = w_uri; + wsinfo.nShow = SW_SHOWNORMAL; + if (clazz != NULL) { + w_clazz = g_utf8_to_utf16(clazz, -1, NULL, NULL, NULL); + wsinfo.fMask |= SEE_MASK_CLASSNAME; + wsinfo.lpClass = w_clazz; + } + + if(!ShellExecuteExW(&wsinfo)) + gaim_debug_error("gtkwgaim", "Error opening URI: %s error: %d\n", + target, (int) wsinfo.hInstApp); + + g_free(w_uri); + g_free(w_verb); + g_free(w_clazz); + } else { + SHELLEXECUTEINFOA sinfo; + gchar *locale_uri; + + locale_uri = g_locale_from_utf8(target, -1, NULL, NULL, NULL); + + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.cbSize = sizeof(sinfo); + sinfo.lpVerb = verb; + sinfo.lpFile = locale_uri; + sinfo.nShow = SW_SHOWNORMAL; + if (clazz != NULL) { + sinfo.fMask |= SEE_MASK_CLASSNAME; + sinfo.lpClass = clazz; + } + + if(!ShellExecuteExA(&sinfo)) + gaim_debug_error("gtkwgaim", "Error opening URI: %s error: %d\n", + target, (int) sinfo.hInstApp); + + g_free(locale_uri); + } + +} + +void gtkwgaim_notify_uri(const char *uri) { + /* We'll allow whatever URI schemes are supported by the + * default http browser. + */ + gtkwgaim_shell_execute(uri, "open", "http"); +} + +#define WM_FOCUS_REQUEST (WM_APP + 13) + +static LRESULT CALLBACK message_window_handler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + + if (msg == WM_FOCUS_REQUEST) { + gaim_debug_info("gtkwgaim", "Got external Buddy List focus request."); + gaim_blist_set_visible(TRUE); + return TRUE; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +static HWND wgaim_message_window_init(void) { + HWND win_hwnd; + WNDCLASSEX wcx; + LPCTSTR wname; + + wname = TEXT("WingaimMsgWinCls"); + + wcx.cbSize = sizeof(wcx); + wcx.style = 0; + wcx.lpfnWndProc = message_window_handler; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = 0; + wcx.hInstance = gtkwgaim_hinstance(); + wcx.hIcon = NULL; + wcx.hCursor = NULL; + wcx.hbrBackground = NULL; + wcx.lpszMenuName = NULL; + wcx.lpszClassName = wname; + wcx.hIconSm = NULL; + + RegisterClassEx(&wcx); + + /* Create the window */ + if(!(win_hwnd = CreateWindow(wname, TEXT("WingaimMsgWin"), 0, 0, 0, 0, 0, + NULL, NULL, gtkwgaim_hinstance(), 0))) { + gaim_debug_error("gtkwgaim", + "Unable to create message window.\n"); + return NULL; + } + + return win_hwnd; +} + +static gboolean stop_flashing(GtkWidget *widget, GdkEventFocus *event, gpointer data) { + GtkWindow *window = data; + gtkwgaim_window_flash(window, FALSE); + return FALSE; +} + +void +gtkwgaim_window_flash(GtkWindow *window, gboolean flash) { + GdkWindow * gdkwin; + + g_return_if_fail(window != NULL); + + gdkwin = GTK_WIDGET(window)->window; + + g_return_if_fail(GDK_IS_WINDOW(gdkwin)); + g_return_if_fail(GDK_WINDOW_TYPE(gdkwin) != GDK_WINDOW_CHILD); + + if(GDK_WINDOW_DESTROYED(gdkwin)) + return; + + if(MyFlashWindowEx) { + FLASHWINFO info; + + memset(&info, 0, sizeof(FLASHWINFO)); + info.cbSize = sizeof(FLASHWINFO); + info.hwnd = GDK_WINDOW_HWND(gdkwin); + if (flash) + info.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG; + else + info.dwFlags = FLASHW_STOP; + info.dwTimeout = 0; + info.dwTimeout = 0; + + MyFlashWindowEx(&info); + } else + FlashWindow(GDK_WINDOW_HWND(gdkwin), flash); +} + +void +gtkwgaim_conv_blink(GaimConversation *conv, GaimMessageFlags flags) { + GaimGtkWindow *win; + GtkWindow *window; + + /* Don't flash for our own messages or system messages */ + if(flags & GAIM_MESSAGE_SEND || flags & GAIM_MESSAGE_SYSTEM) + return; + + if(conv == NULL) { + gaim_debug_info("gtkwgaim", "No conversation found to blink.\n"); + return; + } + + win = gaim_gtkconv_get_window(GAIM_GTK_CONVERSATION(conv)); + if(win == NULL) { + gaim_debug_info("gtkwgaim", "No conversation windows found to blink.\n"); + return; + } + window = GTK_WINDOW(win->window); + + gtkwgaim_window_flash(window, TRUE); + /* Stop flashing when window receives focus */ + g_signal_connect(G_OBJECT(window), "focus-in-event", + G_CALLBACK(stop_flashing), window); +} + +static gboolean +gtkwgaim_conv_im_blink(GaimAccount *account, const char *who, char **message, + GaimConversation *conv, GaimMessageFlags flags, void *data) +{ + if (gaim_prefs_get_bool("/gaim/gtk/win32/blink_im")) + gtkwgaim_conv_blink(conv, flags); + return FALSE; +} + +void gtkwgaim_init(HINSTANCE hint) { + + gaim_debug_info("gtkwgaim", "gtkwgaim_init start\n"); + + gaimexe_hInstance = hint; + + /* IdleTracker Initialization */ + if(!wgaim_set_idlehooks()) + gaim_debug_error("gtkwgaim", "Failed to initialize idle tracker\n"); + + wgaim_gtkspell_init(); + gaim_debug_info("gtkwgaim", "GTK+ :%u.%u.%u\n", + gtk_major_version, gtk_minor_version, gtk_micro_version); + + messagewin_hwnd = wgaim_message_window_init(); + + MyFlashWindowEx = (LPFNFLASHWINDOWEX) wgaim_find_and_loadproc("user32.dll", "FlashWindowEx"); + + gaim_debug_info("gtkwgaim", "gtkwgaim_init end\n"); +} + +void gtkwgaim_post_init(void) { + + gaim_prefs_add_none("/gaim/gtk/win32"); + gaim_prefs_add_bool("/gaim/gtk/win32/blink_im", TRUE); + + gaim_signal_connect(gaim_gtk_conversations_get_handle(), + "displaying-im-msg", >kwin32_handle, GAIM_CALLBACK(gtkwgaim_conv_im_blink), + NULL); + +} + +/* Windows Cleanup */ + +void gtkwgaim_cleanup(void) { + gaim_debug_info("gtkwgaim", "gtkwgaim_cleanup\n"); + + if(messagewin_hwnd) + DestroyWindow(messagewin_hwnd); + + /* Idle tracker cleanup */ + wgaim_remove_idlehooks(); + +} + +/* DLL initializer */ +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { + gtkgaimdll_hInstance = hinstDLL; + return TRUE; +} + +typedef HMONITOR WINAPI gaim_MonitorFromWindow(HWND, DWORD); +typedef BOOL WINAPI gaim_GetMonitorInfo(HMONITOR, LPMONITORINFO); + +static gboolean +get_WorkingAreaRectForWindow(HWND hwnd, RECT *workingAreaRc) { + static gaim_MonitorFromWindow *the_MonitorFromWindow; + static gaim_GetMonitorInfo *the_GetMonitorInfo; + static gboolean initialized = FALSE; + + HMONITOR monitor; + MONITORINFO info; + + if(!initialized) { + the_MonitorFromWindow = (gaim_MonitorFromWindow*) + wgaim_find_and_loadproc("user32", "MonitorFromWindow"); + the_GetMonitorInfo = (gaim_GetMonitorInfo*) + wgaim_find_and_loadproc("user32", "GetMonitorInfoA"); + initialized = TRUE; + } + + if(!the_MonitorFromWindow) + return FALSE; + + if(!the_GetMonitorInfo) + return FALSE; + + monitor = the_MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + + info.cbSize = sizeof(info); + if(!the_GetMonitorInfo(monitor, &info)) + return FALSE; + + CopyRect(workingAreaRc, &(info.rcWork)); + return TRUE; +} + +void gtkwgaim_ensure_onscreen(GtkWidget *win) { + RECT windowRect, workingAreaRect, intersectionRect; + HWND hwnd = GDK_WINDOW_HWND(win->window); + + g_return_if_fail(hwnd != NULL); + GetWindowRect(hwnd, &windowRect); + + gaim_debug_info("win32placement", + "Window RECT: L:%ld R:%ld T:%ld B:%ld\n", + windowRect.left, windowRect.right, + windowRect.top, windowRect.bottom); + + if(!get_WorkingAreaRectForWindow(hwnd, &workingAreaRect)) { + gaim_debug_info("win32placement", + "Couldn't get multimonitor working area\n"); + if(!SystemParametersInfo(SPI_GETWORKAREA, 0, &workingAreaRect, FALSE)) { + /* I don't think this will ever happen */ + workingAreaRect.left = 0; + workingAreaRect.top = 0; + workingAreaRect.bottom = GetSystemMetrics(SM_CYSCREEN); + workingAreaRect.right = GetSystemMetrics(SM_CXSCREEN); + } + } + + gaim_debug_info("win32placement", + "Working Area RECT: L:%ld R:%ld T:%ld B:%ld\n", + workingAreaRect.left, workingAreaRect.right, + workingAreaRect.top, workingAreaRect.bottom); + + /** If the conversation window doesn't intersect perfectly with the working area, + * move it to the top left corner of the working area */ + if(!(IntersectRect(&intersectionRect, &windowRect, &workingAreaRect) + && EqualRect(&intersectionRect, &windowRect))) { + gaim_debug_info("win32placement", + "conversation window out of working area, relocating\n"); + MoveWindow(hwnd, workingAreaRect.left, workingAreaRect.top, + (windowRect.right - windowRect.left), + (windowRect.bottom - windowRect.top), TRUE); + } +} +