/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Jean Le Feuvre
 *			Copyright (c) Telecom ParisTech 2000-2020
 *					All rights reserved
 *
 *  This file is part of GPAC / DirectX audio and video render module
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include "dx_hw.h"
#include <gpac/utf.h>
#include "resource.h"
#include <shellapi.h>

/*crude redef of winuser.h due to windows/winsock2 conflicts*/
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL                   0x020A
#define WHEEL_DELTA                     120
#endif

/*mouse hiding timeout in fullscreen, in milliseconds*/
#define MOUSE_HIDE_TIMEOUT	1000

static const GF_VideoOutput *the_video_output = NULL;

void DD_SetCursor(GF_VideoOutput *dr, u32 cursor_type);


#ifdef _WIN32_WCE
static void DD_GetCoordinates(DWORD lParam, GF_Event *evt)
{
	evt->mouse.x = LOWORD(lParam);
	evt->mouse.y = HIWORD(lParam);
}
#else

static void DD_GetCoordinates(LPARAM lParam, GF_Event *evt)
{
	POINTS pt = MAKEPOINTS(lParam);
	evt->mouse.x = pt.x;
	evt->mouse.y = pt.y;
}
#endif

typedef struct
{
	u32 win_key;
	u32 gf_key;
	u32 gf_flags;
} WINKeyToGPAC;

static WINKeyToGPAC WINKeys[] =
{
	{VK_BACK, GF_KEY_BACKSPACE, 0},
	{VK_TAB, GF_KEY_TAB, 0 },
	{VK_CLEAR, GF_KEY_CLEAR, 0 },
	{VK_RETURN, GF_KEY_ENTER, 0 },
	{VK_SHIFT, GF_KEY_SHIFT, 0 },
	{VK_CONTROL, GF_KEY_CONTROL, 0 },
	{VK_MENU, GF_KEY_ALT, 0 },
	{VK_PAUSE, GF_KEY_PAUSE, 0 },
	{VK_CAPITAL, GF_KEY_CAPSLOCK, 0 },
	{VK_KANA, GF_KEY_KANAMODE, 0 },
	{VK_JUNJA, GF_KEY_JUNJAMODE, 0 },
	{VK_FINAL, GF_KEY_FINALMODE, 0 },
	{VK_KANJI, GF_KEY_KANJIMODE, 0 },
	{VK_ESCAPE, GF_KEY_ESCAPE, 0 },
	{VK_CONVERT, GF_KEY_CONVERT, 0 },
	{VK_SPACE, GF_KEY_SPACE, 0 },
	{VK_PRIOR, GF_KEY_PAGEUP, 0 },
	{VK_NEXT, GF_KEY_PAGEDOWN, 0 },
	{VK_END, GF_KEY_END, 0 },
	{VK_HOME, GF_KEY_HOME, 0 },
	{VK_LEFT, GF_KEY_LEFT, 0 },
	{VK_UP, GF_KEY_UP, 0 },
	{VK_RIGHT, GF_KEY_RIGHT, 0 },
	{VK_DOWN, GF_KEY_DOWN, 0 },
	{VK_SELECT, GF_KEY_SELECT, 0 },
	{VK_PRINT, GF_KEY_PRINTSCREEN, 0 },
	{ VK_SNAPSHOT, GF_KEY_PRINTSCREEN, 0 },
	{VK_EXECUTE, GF_KEY_EXECUTE, 0 },
	{VK_INSERT, GF_KEY_INSERT, 0 },
	{VK_DELETE, GF_KEY_DEL, 0 },
	{VK_HELP, GF_KEY_HELP, 0 },
#if !defined(_WIN32_WCE) && !defined(__GNUC__)
	{VK_OEM_PLUS, GF_KEY_PLUS, 0 },
	{VK_OEM_MINUS, GF_KEY_PLUS, 0 },
#endif
#ifndef _WIN32_WCE
	{VK_NONCONVERT, GF_KEY_NONCONVERT, 0 },
	{VK_ACCEPT, GF_KEY_ACCEPT, 0 },
	{VK_MODECHANGE, GF_KEY_MODECHANGE, 0 },
#endif
	{VK_NUMPAD0, GF_KEY_0, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD1, GF_KEY_1, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD2, GF_KEY_2, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD3, GF_KEY_3, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD4, GF_KEY_4, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD5, GF_KEY_5, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD6, GF_KEY_6, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD7, GF_KEY_7, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD8, GF_KEY_8, GF_KEY_EXT_NUMPAD },
	{VK_NUMPAD9, GF_KEY_9, GF_KEY_EXT_NUMPAD },
	{VK_MULTIPLY, GF_KEY_STAR, GF_KEY_EXT_NUMPAD },
	{VK_ADD, GF_KEY_PLUS, GF_KEY_EXT_NUMPAD },
	{VK_SEPARATOR, GF_KEY_FULLSTOP, GF_KEY_EXT_NUMPAD },
	{VK_SUBTRACT, GF_KEY_HYPHEN, GF_KEY_EXT_NUMPAD },
	{VK_DECIMAL, GF_KEY_COMMA, GF_KEY_EXT_NUMPAD },
	{VK_DIVIDE, GF_KEY_SLASH, GF_KEY_EXT_NUMPAD },
	{VK_F1, GF_KEY_F1, 0},
	{VK_F2, GF_KEY_F2, 0 },
	{VK_F3, GF_KEY_F3, 0 },
	{VK_F4, GF_KEY_F4, 0 },
	{VK_F5, GF_KEY_F5, 0 },
	{VK_F6, GF_KEY_F6, 0 },
	{VK_F7, GF_KEY_F7, 0 },
	{VK_F8, GF_KEY_F8, 0 },
	{VK_F9, GF_KEY_F9, 0 },
	{VK_F10, GF_KEY_F10, 0 },
	{VK_F11, GF_KEY_F11, 0 },
	{VK_F12, GF_KEY_F12, 0 },
	{VK_F13, GF_KEY_F13, 0 },
	{VK_F14, GF_KEY_F14, 0 },
	{VK_F15, GF_KEY_F15, 0 },
	{VK_F16, GF_KEY_F16, 0 },
	{VK_F17, GF_KEY_F17, 0 },
	{VK_F18, GF_KEY_F18, 0 },
	{VK_F19, GF_KEY_F19, 0 },
	{VK_F20, GF_KEY_F20, 0 },
	{VK_F21, GF_KEY_F21, 0 },
	{VK_F22, GF_KEY_F22, 0 },
	{VK_F23, GF_KEY_F23, 0 },
	{VK_F24, GF_KEY_F24, 0 },
	{VK_NUMLOCK, GF_KEY_NUMLOCK, 0 },
	{VK_SCROLL, GF_KEY_SCROLL, 0 },
	{VK_LSHIFT, GF_KEY_SHIFT, GF_KEY_EXT_LEFT},
	{VK_RSHIFT, GF_KEY_SHIFT, GF_KEY_EXT_RIGHT },
	{VK_LCONTROL, GF_KEY_CONTROL, GF_KEY_EXT_LEFT },
	{VK_RCONTROL, GF_KEY_CONTROL, GF_KEY_EXT_RIGHT },
	{VK_LMENU, GF_KEY_ALT, GF_KEY_EXT_LEFT },
	{VK_RMENU, GF_KEY_ALT, GF_KEY_EXT_RIGHT },
#if(WINVER >= 0x0400)
	{VK_PROCESSKEY, GF_KEY_PROCESS, 0},
#endif /* WINVER >= 0x0400 */
	{VK_ATTN, GF_KEY_ATTN, 0 },
	{VK_CRSEL, GF_KEY_CRSEL, 0 },
	{VK_EXSEL, GF_KEY_EXSEL, 0 },
	{VK_EREOF, GF_KEY_ERASEEOF, 0 },
	{VK_PLAY, GF_KEY_PLAY, 0 },
	{VK_ZOOM, GF_KEY_ZOOM, 0 },
	//{VK_NONAME, GF_KEY_NONAME, 0},
	//{VK_PA1, GF_KEY_PA1, 0},
	{VK_OEM_CLEAR, GF_KEY_CLEAR, 0 },
};

static u32 num_win_keys = sizeof(WINKeys) / sizeof(WINKeyToGPAC);

static void w32_translate_key(WPARAM wParam, LPARAM lParam, GF_EventKey *evt)
{
	u32 i;
	evt->flags = 0;
	evt->hw_code = (u32)wParam;

	for (i = 0; i<num_win_keys; i++) {
		if (WINKeys[i].win_key == (u32) wParam) {
			evt->key_code = WINKeys[i].gf_key;
			evt->flags = WINKeys[i].gf_flags;
			return;
		}
	}

	if ((wParam>=0x30) && (wParam<=0x39))
		evt->key_code = GF_KEY_0 + (u32) (wParam-0x30);
	else if ((wParam>=0x41) && (wParam<=0x5A))
		evt->key_code = GF_KEY_A + (u32) (wParam-0x41);
	/*DOM 3 Events: Implementations that are unable to identify a key must use the key identifier "Unidentified".*/
	else {
		GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[DX] Unrecognized key %X\n", wParam));
		evt->key_code = GF_KEY_UNIDENTIFIED;
	}
}


static void mouse_move(DDContext *ctx, GF_VideoOutput *vout)
{
	if (ctx->fullscreen) {
		ctx->last_mouse_move = gf_sys_clock();
		if (ctx->cursor_type==GF_CURSOR_HIDE) DD_SetCursor(vout, ctx->cursor_type_backup);
	}
}

static void mouse_start_timer(DDContext *ctx, HWND hWnd, GF_VideoOutput *vout)
{
	if (ctx->fullscreen) {
		if (!ctx->timer) ctx->timer = SetTimer(hWnd, 10, 1000, NULL);
		mouse_move(ctx, vout);
	}
}

void grab_mouse(DDContext *ctx, GF_VideoOutput *vout)
{
	if (ctx->fullscreen) DD_SetCursor(vout, GF_CURSOR_NORMAL);
	SetCapture(ctx->cur_hwnd);
	mouse_move(ctx, vout);
}
void release_mouse(DDContext *ctx, HWND hWnd, GF_VideoOutput *vout)
{
	ReleaseCapture();
	mouse_start_timer(ctx, hWnd, vout);
}

LRESULT APIENTRY DD_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	Bool ret = GF_TRUE;
	GF_Event evt;
	DDContext *ctx;
#ifdef _WIN64
	GF_VideoOutput *vout = (GF_VideoOutput *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
	GF_VideoOutput *vout = (GF_VideoOutput *) GetWindowLong(hWnd, GWL_USERDATA);
#endif
	if (!vout) return DefWindowProc(hWnd, msg, wParam, lParam);
	ctx = (DDContext *)vout->opaque;

	switch (msg) {
	case WM_SIZE:
		/*always notify GPAC since we're not sure the owner of the window is listening to these events*/
		if (wParam==SIZE_MINIMIZED) {
			evt.type = GF_EVENT_SHOWHIDE;
			evt.show.show_type = 0;
			ctx->hidden = GF_TRUE;
			vout->on_event(vout->evt_cbk_hdl, &evt);
		} else {
			if (ctx->hidden && wParam==SIZE_RESTORED) {
				ctx->hidden = GF_FALSE;
				evt.type = GF_EVENT_SHOWHIDE;
				evt.show.show_type = 1;
				vout->on_event(vout->evt_cbk_hdl, &evt);
			}
			evt.type = GF_EVENT_SIZE;
			evt.size.width = LOWORD(lParam);
			evt.size.height = HIWORD(lParam);
			vout->on_event(vout->evt_cbk_hdl, &evt);
		}
		break;
	case WM_MOVE:
		evt.type = GF_EVENT_MOVE;
		evt.move.x = LOWORD(lParam);
		evt.move.y = HIWORD(lParam);
		vout->on_event(vout->evt_cbk_hdl, &evt);
		break;
	case WM_CLOSE:
		if (hWnd==ctx->os_hwnd) {
			memset(&evt, 0, sizeof(GF_Event));
			evt.type = GF_EVENT_QUIT;
			vout->on_event(vout->evt_cbk_hdl, &evt);
		}
		break;
	case WM_DESTROY:
		if (ctx->owns_hwnd || (hWnd==ctx->fs_hwnd)) {
			PostQuitMessage (0);
		} else if (ctx->orig_wnd_proc) {
			/*restore window proc*/
#ifdef _WIN64
			SetWindowLongPtr(ctx->os_hwnd, GWLP_WNDPROC, ctx->orig_wnd_proc);
#else
			SetWindowLong(ctx->os_hwnd, GWL_WNDPROC, ctx->orig_wnd_proc);
#endif
			ctx->orig_wnd_proc = 0L;
		}
		break;
	case WM_DISPLAYCHANGE:
		ctx->dd_lost = GF_TRUE;
		memset(&evt, 0, sizeof(GF_Event));
		evt.type = GF_EVENT_VIDEO_SETUP;
		evt.setup.back_buffer = GF_TRUE;
		vout->on_event(vout->evt_cbk_hdl, &evt);
		break;

	case WM_ACTIVATE:
		if (!ctx->on_secondary_screen && ctx->fullscreen && (LOWORD(wParam)==WA_INACTIVE)
		        && (hWnd==ctx->fs_hwnd)
		   ) {
			evt.type = GF_EVENT_SHOWHIDE;
			vout->on_event(vout->evt_cbk_hdl, &evt);
		}
		/*fallthrough*/
#ifndef _WIN32_WCE
	case WM_ACTIVATEAPP:
#endif
		if (hWnd==ctx->os_hwnd) {
			ctx->has_focus = GF_TRUE;
			SetFocus(hWnd);
		}
		break;
	case WM_KILLFOCUS:
		if (hWnd==ctx->os_hwnd) ctx->has_focus = GF_FALSE;
		break;
	case WM_SETFOCUS:
		if (hWnd==ctx->os_hwnd) ctx->has_focus = GF_TRUE;
		break;
	case WM_IME_SETCONTEXT:
		if ((hWnd==ctx->os_hwnd) && (wParam!=0) && !ctx->fullscreen) SetFocus(hWnd);
		break;

	case WM_ERASEBKGND:
		/*the erasebkgnd message causes flickering when resizing the window, we discard it*/
		if (ctx->is_setup) {
			break;
		}
	case WM_PAINT:
		if (ctx->cur_hwnd==hWnd) {
			evt.type = GF_EVENT_REFRESH;
			vout->on_event(vout->evt_cbk_hdl, &evt);
		}
		/*this avoids 100% cpu usage in Firefox*/
		return DefWindowProc (hWnd, msg, wParam, lParam);

	case WM_SETCURSOR:
		if (ctx->cur_hwnd==hWnd) DD_SetCursor(vout, ctx->cursor_type);
		break;

	case WM_DROPFILES:
	{
		char szFile[GF_MAX_PATH];
		u32 i;

		HDROP hDrop = (HDROP) wParam;
		evt.type = GF_EVENT_DROPFILE;
		evt.open_file.nb_files = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
		evt.open_file.files = (char**)gf_malloc(sizeof(char *)*evt.open_file.nb_files);
		for (i=0; i<evt.open_file.nb_files; i++) {
#ifdef UNICODE
			/* TODO: Fix by converting to WC */
			u32 res = DragQueryFile(hDrop, i, szFile, GF_MAX_PATH);
#else
			u32 res = DragQueryFile(hDrop, i, szFile, GF_MAX_PATH);
#endif
			evt.open_file.files[i] = res ? gf_strdup(szFile) : NULL;
		}
		DragFinish(hDrop);
		/*send message*/
		vout->on_event(vout->evt_cbk_hdl, &evt);
		for (i=0; i<evt.open_file.nb_files; i++) {
			if (evt.open_file.files[i]) gf_free(evt.open_file.files[i]);
		}
		gf_free(evt.open_file.files);
	}
	break;

	case WM_MOUSEMOVE:
		if (ctx->cur_hwnd!=hWnd) break;

		if (ctx->last_mouse_pos != lParam) {
			ctx->last_mouse_pos = lParam;
			DD_SetCursor(vout, (ctx->cursor_type==GF_CURSOR_HIDE) ? ctx->cursor_type_backup : ctx->cursor_type);
			evt.type = GF_EVENT_MOUSEMOVE;
			DD_GetCoordinates(lParam, &evt);
			ret = vout->on_event(vout->evt_cbk_hdl, &evt);
			mouse_start_timer(ctx, hWnd, vout);
		}
		break;

	case WM_TIMER:
		if (wParam==10) {
			if (ctx->fullscreen && (ctx->cursor_type!=GF_CURSOR_HIDE)) {
				if (gf_sys_clock() > MOUSE_HIDE_TIMEOUT + ctx->last_mouse_move) {
					ctx->cursor_type_backup = ctx->cursor_type;
					DD_SetCursor(vout, GF_CURSOR_HIDE);
					KillTimer(hWnd, ctx->timer);
					ctx->timer = 0;
				}
			}
		}
		break;
	case WM_LBUTTONDOWN:
	case WM_LBUTTONDBLCLK:
		grab_mouse(ctx, vout);
		evt.type = GF_EVENT_MOUSEDOWN;
		DD_GetCoordinates(lParam, &evt);
		evt.mouse.button = GF_MOUSE_LEFT;
		ret = vout->on_event(vout->evt_cbk_hdl, &evt);
		if (!ctx->has_focus && (hWnd==ctx->os_hwnd)) {
			ctx->has_focus = GF_TRUE;
			SetFocus(ctx->os_hwnd);
		}
		break;
	case WM_LBUTTONUP:
		release_mouse(ctx, hWnd, vout);
		evt.type = GF_EVENT_MOUSEUP;
		DD_GetCoordinates(lParam, &evt);
		evt.mouse.button = GF_MOUSE_LEFT;
		ret = vout->on_event(vout->evt_cbk_hdl, &evt);
		break;
	case WM_RBUTTONDOWN:
	case WM_RBUTTONDBLCLK:
		grab_mouse(ctx, vout);
		evt.type = GF_EVENT_MOUSEDOWN;
		DD_GetCoordinates(lParam, &evt);
		evt.mouse.button = GF_MOUSE_RIGHT;
		ret = vout->on_event(vout->evt_cbk_hdl, &evt);
		if (!ctx->has_focus && (hWnd==ctx->os_hwnd)) {
			ctx->has_focus = GF_TRUE;
			SetFocus(ctx->os_hwnd);
		}
		break;
	case WM_RBUTTONUP:
		release_mouse(ctx, hWnd, vout);
		evt.type = GF_EVENT_MOUSEUP;
		DD_GetCoordinates(lParam, &evt);
		evt.mouse.button = GF_MOUSE_RIGHT;
		ret = vout->on_event(vout->evt_cbk_hdl, &evt);
		mouse_start_timer(ctx, hWnd, vout);
		break;
	case WM_MBUTTONDOWN:
	case WM_MBUTTONDBLCLK:
		grab_mouse(ctx, vout);
		evt.type = GF_EVENT_MOUSEDOWN;
		evt.mouse.button = GF_MOUSE_MIDDLE;
		DD_GetCoordinates(lParam, &evt);
		ret = vout->on_event(vout->evt_cbk_hdl, &evt);
		if (!ctx->has_focus && (hWnd==ctx->os_hwnd)) {
			ctx->has_focus = GF_TRUE;
			SetFocus(ctx->os_hwnd);
		}
		break;
	case WM_MBUTTONUP:
		release_mouse(ctx, hWnd, vout);
		evt.type = GF_EVENT_MOUSEUP;
		DD_GetCoordinates(lParam, &evt);
		evt.mouse.button = GF_MOUSE_MIDDLE;
		ret = vout->on_event(vout->evt_cbk_hdl, &evt);
		mouse_start_timer(ctx, hWnd, vout);
		break;
	case WM_MOUSEWHEEL:
		if (ctx->cur_hwnd==hWnd) {
			DD_SetCursor(vout, (ctx->cursor_type==GF_CURSOR_HIDE) ? ctx->cursor_type_backup : ctx->cursor_type);
			evt.type = GF_EVENT_MOUSEWHEEL;
			DD_GetCoordinates(lParam, &evt);
			evt.mouse.wheel_pos = FLT2FIX( ((Float) (s16) HIWORD(wParam)) / WHEEL_DELTA );
			ret = vout->on_event(vout->evt_cbk_hdl, &evt);
			mouse_start_timer(ctx, hWnd, vout);
		}
		break;
	case WM_HSCROLL:
		if (ctx->cur_hwnd==hWnd) {
			DD_SetCursor(vout, (ctx->cursor_type==GF_CURSOR_HIDE) ? ctx->cursor_type_backup : ctx->cursor_type);
			evt.type = GF_EVENT_MOUSEWHEEL;
			evt.mouse.button = 1;
			switch (LOWORD(wParam)) {
			case SB_LEFT:
				evt.mouse.wheel_pos = -1;
				break;
			case SB_RIGHT:
				evt.mouse.wheel_pos = +1;
				break;
			case SB_LINELEFT:
				evt.mouse.wheel_pos = -5;
				break;
			case SB_LINERIGHT:
				evt.mouse.wheel_pos = +5;
				break;
			case SB_PAGELEFT:
				evt.mouse.wheel_pos = -10;
				break;
			case SB_PAGERIGHT:
				evt.mouse.wheel_pos = +10;
				break;
			default:
				break;
			}
			ret = vout->on_event(vout->evt_cbk_hdl, &evt);
			mouse_start_timer(ctx, hWnd, vout);
		}
		break;

	/*there's a bug on alt state (we miss one event)*/
	case WM_SYSKEYDOWN:
	case WM_SYSKEYUP:
	case WM_KEYUP:
	case WM_KEYDOWN:
		w32_translate_key(wParam, lParam, &evt.key);
		evt.type = ((msg==WM_SYSKEYDOWN) || (msg==WM_KEYDOWN)) ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP;
		if (evt.key.key_code==GF_KEY_ALT) ctx->alt_down = (evt.type==GF_EVENT_KEYDOWN) ? GF_TRUE : GF_FALSE;
		if (evt.key.key_code==GF_KEY_CONTROL) ctx->ctrl_down = (evt.type==GF_EVENT_KEYDOWN) ? GF_TRUE : GF_FALSE;
		if ((ctx->os_hwnd==ctx->fs_hwnd) && ctx->alt_down && (evt.key.key_code==GF_KEY_F4)) {
			memset(&evt, 0, sizeof(GF_Event));
			evt.type = GF_EVENT_QUIT;
		}
		else if (ctx->ctrl_down && (evt.type==GF_EVENT_KEYUP) && (evt.key.key_code==GF_KEY_V)) {
			HGLOBAL hglbCopy;
			if (!IsClipboardFormatAvailable(CF_TEXT)) break;
			if (!OpenClipboard(ctx->cur_hwnd)) break;

			hglbCopy = GetClipboardData(CF_TEXT);
			if (hglbCopy) {
#ifdef UNICODE
				LPTSTR lptstrCopy = (wchar_t *) GlobalLock(hglbCopy);
				evt.type = GF_EVENT_PASTE_TEXT;
				/* TODO: Convert to UTF-8 */
				evt.clipboard.text = lptstrCopy;
#else
				LPTSTR lptstrCopy = (char *)GlobalLock(hglbCopy);
				evt.type = GF_EVENT_PASTE_TEXT;
				evt.clipboard.text = lptstrCopy;
#endif
				ret = vout->on_event(vout->evt_cbk_hdl, &evt);
				GlobalUnlock(hglbCopy);
			}
			CloseClipboard();
			break;
		}
		else if (ctx->ctrl_down && (evt.type==GF_EVENT_KEYUP) && (evt.key.key_code==GF_KEY_C)) {
			evt.type = GF_EVENT_COPY_TEXT;
			if ((vout->on_event(vout->evt_cbk_hdl, &evt)==GF_TRUE) && evt.clipboard.text) {
				size_t len;
				HGLOBAL hglbCopy;
				LPTSTR lptstrCopy;
				if (!IsClipboardFormatAvailable(CF_TEXT)) break;
				if (!OpenClipboard(ctx->cur_hwnd)) break;
				EmptyClipboard();

				len = strlen(evt.clipboard.text);
				if (!len) break;

				hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (len + 1) * sizeof(char));
#ifdef UNICODE
				/* TODO fix encoding*/
				lptstrCopy = (wchar_t *)GlobalLock(hglbCopy);
				memcpy(lptstrCopy, evt.clipboard.text, len * sizeof(char));
				lptstrCopy[len] = 0;
#else
				lptstrCopy = (char *) GlobalLock(hglbCopy);
				memcpy(lptstrCopy, evt.clipboard.text, len * sizeof(char));
				lptstrCopy[len] = 0;
#endif
				GlobalUnlock(hglbCopy);
				SetClipboardData(CF_TEXT, hglbCopy);
				CloseClipboard();
				gf_free(evt.clipboard.text);
				break;
			}
		}
		ret = vout->on_event(vout->evt_cbk_hdl, &evt);

		if ( !ctx->ctrl_down && !ctx->alt_down
		        && evt.key.key_code != GF_KEY_CONTROL && evt.key.key_code != GF_KEY_ALT )
			ret = GF_TRUE;
		break;

	case WM_UNICHAR:
	case WM_CHAR:
		evt.type = GF_EVENT_TEXTINPUT;
		evt.character.unicode_char = (u32) wParam;
		ret = vout->on_event(vout->evt_cbk_hdl, &evt);
		break;
	/*
		case WM_CANCELMODE:
		case WM_CAPTURECHANGED:
		case WM_NCHITTEST:
			return 0;
	*/
	default:
		return DefWindowProc (hWnd, msg, wParam, lParam);
	}

	if (!ret &&(ctx->os_hwnd==hWnd) && ctx->orig_wnd_proc)
		return CallWindowProc((WNDPROC) ctx->orig_wnd_proc, hWnd, msg, wParam, lParam);
	return 0;
}

#ifndef WS_EX_LAYERED
#define WS_EX_LAYERED 0x80000
#endif
#ifndef LWA_COLORKEY
#define LWA_COLORKEY   0x00000001
#endif
#ifndef LWA_ALPHA
#define LWA_ALPHA      0x00000002
#endif
typedef BOOL (WINAPI* typSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);


static void SetWindowless(GF_VideoOutput *vout, HWND hWnd)
{
#ifdef _WIN32_WCE
	GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DX Out] Windowloess mode not supported on Window CE\n"));
	return;
#else
/* use VerifyVersionInfo() instead of GetVersionEx? */
#if	!defined(__GNUC__)
#pragma warning(disable : 4996)
#endif
	const char *opt;
	u32 a, r, g, b;
	COLORREF ckey;
	typSetLayeredWindowAttributes _SetLayeredWindowAttributes;
	HMODULE hUser32;
	u32 isWin2K;
	OSVERSIONINFO Version = {sizeof(OSVERSIONINFO)};
	GetVersionEx(&Version);
	isWin2K = (Version.dwPlatformId == VER_PLATFORM_WIN32_NT && Version.dwMajorVersion >= 5);
	if (!isWin2K) return;

	GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Enabling windowless mode\n"));
#ifdef UNICODE
	hUser32 = GetModuleHandle(L"USER32.DLL");
#else
	hUser32 = GetModuleHandle("USER32.DLL");
#endif
	if (hUser32 == NULL) return;

	_SetLayeredWindowAttributes = (typSetLayeredWindowAttributes) GetProcAddress(hUser32,"SetLayeredWindowAttributes");
	if (_SetLayeredWindowAttributes == NULL) {
		GF_LOG(GF_LOG_WARNING, GF_LOG_MMIO, ("[DX Out] Win32 layered windows not supported\n"));
		return;
	}

	SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);

	/*get background ckey*/
	opt = gf_opts_get_key("filter@compositor", "ckey");
	if (!opt) {
		gf_opts_set_key("filter@compositor", "ckey", "FFFEFEFE");
		opt = "FFFEFEFE";
	}

	sscanf(opt, "%02X%02X%02X%02X", &a, &r, &g, &b);
	ckey = RGB(r, g, b);
	if (a<255)
		_SetLayeredWindowAttributes(hWnd, ckey, (u8) a, LWA_COLORKEY|LWA_ALPHA);
	else
		_SetLayeredWindowAttributes(hWnd, ckey, 0, LWA_COLORKEY);

	GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Using color key %s\n", opt));
#endif
}


Bool DD_InitWindows(GF_VideoOutput *vout, DDContext *ctx)
{
	u32 flags;
	Bool use_fs_wnd = GF_TRUE;
#ifndef _WIN32_WCE
	RECT rc;
#endif
	WNDCLASS wc;
	HINSTANCE hInst;

#ifndef _WIN32_WCE
#ifdef UNICODE
	hInst = GetModuleHandle(L"gm_dx_hw.dll");
#else
	hInst = GetModuleHandle("gm_dx_hw.dll");
#endif
#else
	hInst = GetModuleHandle(_T("gm_dx_hw.dll"));
#endif

	memset(&wc, 0, sizeof(WNDCLASS));
#ifndef _WIN32_WCE
	wc.style = CS_BYTEALIGNWINDOW;
	wc.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(IDI_GPAC_ICON) );
#ifdef UNICODE
	wc.lpszClassName = L"GPAC DirectDraw Output";
#else
	wc.lpszClassName = "GPAC DirectDraw Output";
#endif
#else
	wc.lpszClassName = _T("GPAC DirectDraw Output");
#endif
	wc.hInstance = hInst;
	wc.lpfnWndProc = DD_WindowProc;
	wc.hCursor = LoadCursor (NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH);
	RegisterClass (&wc);

	flags = ctx->switch_res;
	ctx->switch_res = GF_FALSE;
	ctx->force_alpha = (flags & GF_VOUT_WINDOW_NO_DECORATION) ? GF_TRUE : GF_FALSE;

	if (!ctx->os_hwnd) {
#ifndef _WIN32_WCE
		u32 styles;
#endif
		if (flags & GF_VOUT_WINDOWLESS) ctx->windowless = GF_TRUE;


#ifdef _WIN32_WCE
		ctx->os_hwnd = CreateWindow(_T("GPAC DirectDraw Output"), _T("GPAC DirectDraw Output"), WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL);
#else

		if (flags & GF_VOUT_WINDOW_NO_DECORATION) {
			styles = WS_POPUP | WS_THICKFRAME;
		} else if (ctx->windowless) {
			styles = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
		} else {
			styles = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW;
		}
		use_fs_wnd = GF_FALSE;
#ifdef UNICODE
		ctx->os_hwnd = CreateWindow(L"GPAC DirectDraw Output", L"GPAC DirectDraw Output", styles, 0, 0, 120, 100, NULL, NULL, hInst, NULL);
#else
		ctx->os_hwnd = CreateWindow("GPAC DirectDraw Output", "GPAC DirectDraw Output", styles, 0, 0, 120, 100, NULL, NULL, hInst, NULL);
#endif
		ctx->backup_styles = styles;
#endif
		if (ctx->os_hwnd == NULL) {
			return GF_FALSE;
		}
		DragAcceptFiles(ctx->os_hwnd, TRUE);

		if (flags & GF_VOUT_INIT_HIDE) {
			ShowWindow(ctx->os_hwnd, SW_HIDE);
		} else {
			SetForegroundWindow(ctx->os_hwnd);
			ShowWindow(ctx->os_hwnd, SW_SHOW);
		}

		/*get border & title bar sizes*/
#ifdef _WIN32_WCE
		ctx->off_w = 0;
		ctx->off_h = 0;
#else
		rc.left = rc.top = 0;
		rc.right = rc.bottom = 100;
		AdjustWindowRect(&rc, styles, 0);
		ctx->off_w = rc.right - rc.left - 100;
		ctx->off_h = rc.bottom - rc.top - 100;
#endif
		ctx->owns_hwnd = GF_TRUE;

		if (ctx->windowless) SetWindowless(vout, ctx->os_hwnd);
	}

	if (use_fs_wnd) {
#ifdef _WIN32_WCE
		ctx->fs_hwnd = CreateWindow(_T("GPAC DirectDraw Output"), _T("GPAC DirectDraw FS Output"), WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL);
#else
#ifdef UNICODE
		ctx->fs_hwnd = CreateWindow(L"GPAC DirectDraw Output", L"GPAC DirectDraw FS Output", WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL);
#else
		ctx->fs_hwnd = CreateWindow("GPAC DirectDraw Output", "GPAC DirectDraw FS Output", WS_POPUP, 0, 0, 120, 100, NULL, NULL, hInst, NULL);
#endif
#endif
		if (!ctx->fs_hwnd) {
			return GF_FALSE;
		}
		ShowWindow(ctx->fs_hwnd, SW_HIDE);
#ifdef _WIN64
		SetWindowLongPtr(ctx->fs_hwnd, GWLP_USERDATA, (LONG_PTR) vout);
#else
		SetWindowLong(ctx->fs_hwnd, GWL_USERDATA, (LONG) vout);
#endif
	} else {
		ctx->fs_hwnd = ctx->os_hwnd;
	}

	/*if visible set focus*/
	if (!ctx->switch_res) SetFocus(ctx->os_hwnd);

	ctx->switch_res = GF_FALSE;
#ifdef _WIN64
	SetWindowLongPtr(ctx->os_hwnd, GWLP_USERDATA, (LONG_PTR) vout);
#else
	SetWindowLong(ctx->os_hwnd, GWL_USERDATA, (LONG) vout);
#endif

	/*load cursors*/
	ctx->curs_normal = LoadCursor(NULL, IDC_ARROW);
	ctx->curs_hand = LoadCursor(hInst, MAKEINTRESOURCE(IDC_HAND_PTR));
	ctx->curs_collide = LoadCursor(hInst, MAKEINTRESOURCE(IDC_COLLIDE));
	ctx->cursor_type = GF_CURSOR_NORMAL;
	return GF_TRUE;
}

u32 DD_WindowThread(void *par)
{
	MSG msg;

	GF_VideoOutput *vout = (GF_VideoOutput*)par;
	DDContext *ctx = (DDContext *)vout->opaque;

	GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[DirectXOutput] Entering thread ID %d\n", gf_th_id() ));

	if (DD_InitWindows(vout, ctx)) {
		s32 msg_ok=1;
		ctx->th_state = 1;
		while (msg_ok) {
			msg_ok = GetMessage (&(msg), NULL, 0, 0);
			if (msg_ok == -1) msg_ok = 0;
			if (msg.message == WM_DESTROY) PostQuitMessage(0);	//WM_DESTROY: exit
			TranslateMessage (&(msg));
			DispatchMessage (&(msg));

			if (ctx->caption) {
#ifdef UNICODE
				char *str_src = ctx->caption;
				wchar_t *wcaption;
				size_t len;
				size_t len_res;
				len = ((strlen(str_src)/2)*2 + 2) * sizeof(wchar_t);
				wcaption = (wchar_t *)gf_malloc(len);
				len_res = gf_utf8_mbstowcs(wcaption, len+1, &str_src);
				if (len_res != -1) {
					SetWindowTextW(ctx->os_hwnd, wcaption);
				}
				gf_free(wcaption);
#else
				SetWindowText(ctx->os_hwnd, ctx->caption);
#endif
				gf_free(ctx->caption);
				ctx->caption = NULL;
			}

		}
	}
	ctx->th_state = 2;
	return 0;
}


void DD_SetupWindow(GF_VideoOutput *dr, u32 flags)
{
	DDContext *ctx = (DDContext *)dr->opaque;

	if (ctx->os_hwnd) {
		/*override window proc*/
		if (!(flags & GF_VOUT_NO_WINDOWPROC_OVERRIDE) ) {
#ifdef _WIN64
			ctx->orig_wnd_proc = GetWindowLongPtr(ctx->os_hwnd, GWLP_WNDPROC);
			SetWindowLongPtr(ctx->os_hwnd, GWLP_WNDPROC, (LONG_PTR) DD_WindowProc);
#else
			ctx->orig_wnd_proc = GetWindowLong(ctx->os_hwnd, GWL_WNDPROC);
			SetWindowLong(ctx->os_hwnd, GWL_WNDPROC, (LONG) DD_WindowProc);
#endif
		}
		ctx->parent_wnd = GetParent(ctx->os_hwnd);
	}
	ctx->switch_res = flags;

	if (flags & GF_VOUT_WINDOW_NO_THREAD) {
		DD_InitWindows(dr, ctx);
	} else {
		/*create event thread*/
		ctx->th = gf_th_new("DirectX Video");
		gf_th_run(ctx->th, DD_WindowThread, dr);
		while (!ctx->th_state)
			gf_sleep(1);
	}
	if (!the_video_output) the_video_output = dr;
}

static void dd_closewindow(HWND hWnd)
{
	PostMessage(hWnd, WM_DESTROY, 0, 0);
}

void DD_ShutdownWindow(GF_VideoOutput *dr)
{
	DDContext *ctx = (DDContext *)dr->opaque;

	if (ctx->owns_hwnd) {
		dd_closewindow(ctx->os_hwnd);
	} else if (ctx->orig_wnd_proc) {
		/*restore window proc*/
#ifdef _WIN64
		SetWindowLongPtr(ctx->os_hwnd, GWLP_WNDPROC, ctx->orig_wnd_proc);
#else
		SetWindowLong(ctx->os_hwnd, GWL_WNDPROC, ctx->orig_wnd_proc);
#endif
		ctx->orig_wnd_proc = 0L;
	}

	if (ctx->fs_hwnd != ctx->os_hwnd) {
		dd_closewindow(ctx->fs_hwnd);
#ifdef _WIN64
		SetWindowLongPtr(ctx->fs_hwnd, GWLP_USERDATA, (LONG_PTR) NULL);
		SetWindowLongPtr(ctx->fs_hwnd, GWLP_WNDPROC, (LONG_PTR) DefWindowProc);
#else
		SetWindowLong(ctx->fs_hwnd, GWL_USERDATA, (LONG) NULL);
		SetWindowLong(ctx->fs_hwnd, GWL_WNDPROC, (DWORD) DefWindowProc);
#endif
	}

	if (ctx->th) {
		while (ctx->th_state!=2)
			gf_sleep(10);

		gf_th_del(ctx->th);
		ctx->th = NULL;
	}

	/*special care for Firefox: the windows created by our NP plugin may still be called
	after the shutdown of the plugin !!*/
#ifdef _WIN64
	SetWindowLongPtr(ctx->os_hwnd, GWLP_USERDATA, (LONG_PTR) NULL);
#else
	SetWindowLong(ctx->os_hwnd, GWL_USERDATA, (LONG) NULL);
#endif

#ifdef _WIN32_WCE
	UnregisterClass(_T("GPAC DirectDraw Output"), GetModuleHandle(_T("gm_dx_hw.dll")) );
#else
#ifdef UNICODE
	UnregisterClass(L"GPAC DirectDraw Output", GetModuleHandle(L"gm_dx_hw.dll"));
#else
	UnregisterClass("GPAC DirectDraw Output", GetModuleHandle("gm_dx_hw.dll"));
#endif
#endif
	ctx->os_hwnd = NULL;
	ctx->fs_hwnd = NULL;
	the_video_output = NULL;
}

void DD_SetCursor(GF_VideoOutput *dr, u32 cursor_type)
{
	DDContext *ctx = (DDContext *)dr->opaque;
	if (cursor_type==GF_CURSOR_HIDE) {
		if (ctx->cursor_type!=GF_CURSOR_HIDE) {
			ShowCursor(FALSE);
			ctx->cursor_type = cursor_type;
		}
		return;
	}
	if (ctx->cursor_type==GF_CURSOR_HIDE) ShowCursor(TRUE);
	ctx->cursor_type = cursor_type;

	switch (cursor_type) {
	case GF_CURSOR_ANCHOR:
	case GF_CURSOR_TOUCH:
	case GF_CURSOR_ROTATE:
	case GF_CURSOR_PROXIMITY:
	case GF_CURSOR_PLANE:
		SetCursor(ctx->curs_hand);
		break;
	case GF_CURSOR_COLLIDE:
		SetCursor(ctx->curs_collide);
		break;
	default:
		SetCursor(ctx->curs_normal);
		break;
	}
}

HWND DD_GetGlobalHWND()
{
	if (!the_video_output) return NULL;
	return ((DDContext*)the_video_output->opaque)->os_hwnd;
}

static u32 get_sys_col(int idx)
{
	u32 res;
	DWORD val = GetSysColor(idx);
	res = (val)&0xFF;
	res<<=8;
	res |= (val>>8)&0xFF;
	res<<=8;
	res |= (val>>16)&0xFF;
	return res;
}


/*Note: all calls to SetWindowPos are made in a non-blocking way using SWP_ASYNCWINDOWPOS. This avoids deadlocks
when the compositor request a size change and the DX window thread has grabbed the main compositor mutex.
This typically happens when switching playlist items as fast as possible*/
GF_Err DD_ProcessEvent(GF_VideoOutput*dr, GF_Event *evt)
{
	DDContext *ctx = (DDContext *)dr->opaque;

	if (!evt) {
		if (!ctx->th) {
			MSG msg;

			if (ctx->caption) {
#ifdef UNICODE
				/* TODO: Fix by converting UTF-8 to WC */
				SetWindowText(ctx->os_hwnd, ctx->caption);
#else
				SetWindowText(ctx->os_hwnd, ctx->caption);
#endif
				gf_free(ctx->caption);
				ctx->caption = NULL;
			}

			while (PeekMessage (&(msg), NULL, 0, 0, PM_REMOVE)) {
				TranslateMessage (&(msg));
				DispatchMessage (&(msg));
			}
		}
		return GF_OK;
	}

	switch (evt->type) {
	case GF_EVENT_SET_CURSOR:
		DD_SetCursor(dr, evt->cursor.cursor_type);
		break;
	case GF_EVENT_SET_CAPTION:
#ifndef _WIN32_WCE
		if (evt->caption.caption && !ctx->caption) ctx->caption = gf_strdup(evt->caption.caption);
#endif
		break;
	case GF_EVENT_MOVE:
		if (evt->move.relative == 2) {
			u32 x, y, fsw, fsh;
			x = y = 0;
			fsw = GetSystemMetrics(SM_CXSCREEN);
			fsh = GetSystemMetrics(SM_CYSCREEN);

			if (evt->move.align_x==1) x = (fsw - ctx->width) / 2;
			else if (evt->move.align_x==2) x = fsw - ctx->width;

			if (evt->move.align_y==1) y = (fsh - ctx->height) / 2;
			else if (evt->move.align_y==2) y = fsh - ctx->height;

			SetWindowPos(ctx->os_hwnd, NULL, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS);
		}
		else if (evt->move.relative) {
			POINT pt;
			pt.x = pt.y = 0;
			MapWindowPoints(ctx->os_hwnd, NULL, &pt, 1);
			SetWindowPos(ctx->os_hwnd, NULL, evt->move.x + pt.x, evt->move.y + pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS);
		} else {
			SetWindowPos(ctx->os_hwnd, NULL, evt->move.x, evt->move.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_ASYNCWINDOWPOS);
		}
		break;
	case GF_EVENT_SHOWHIDE:
		ShowWindow(ctx->os_hwnd, evt->show.show_type ? SW_SHOW : SW_HIDE);
		break;
	/*if scene resize resize window*/
	case GF_EVENT_SIZE:
		if (ctx->owns_hwnd) {
			if (ctx->fullscreen) {
				ctx->store_width = evt->size.width;
				ctx->store_height = evt->size.height;
			} else {
				if (ctx->windowless)
					SetWindowPos(ctx->os_hwnd, NULL, 0, 0, evt->size.width, evt->size.height, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS);
				else
					SetWindowPos(ctx->os_hwnd, NULL, 0, 0, evt->size.width + ctx->off_w, evt->size.height + ctx->off_h, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS);
			}
		}
		break;
	case GF_EVENT_SET_GL:
#ifndef GPAC_DISABLE_3D
		if (ctx->pb_HDC) {
			if (!wglMakeCurrent(ctx->pb_HDC, ctx->pb_HRC))
				return GF_IO_ERR;
			return GF_OK;
		}
		if (!wglMakeCurrent(ctx->gl_HDC, ctx->gl_HRC))
			return GF_IO_ERR;
		return GF_OK;
#endif
	/*HW setup*/
	case GF_EVENT_VIDEO_SETUP:
		if (ctx->dd_lost) {
			ctx->dd_lost = GF_FALSE;
			DestroyObjects(ctx);
		}
		ctx->is_setup = GF_TRUE;
		if (!evt->setup.use_opengl) {
#ifndef GPAC_DISABLE_3D
			ctx->output_3d = GF_FALSE;
#endif
			return DD_SetBackBufferSize(dr, evt->setup.width, evt->setup.height, evt->setup.system_memory);
		} else {
#ifndef GPAC_DISABLE_3D
			ctx->output_3d = GF_TRUE;
			ctx->width = evt->setup.width;
			ctx->height = evt->setup.height;
			ctx->gl_double_buffer = evt->setup.back_buffer;
			return DD_SetupOpenGL(dr, 0, 0);
#else
			return GF_NOT_SUPPORTED;
#endif
		}

	case GF_EVENT_SYS_COLORS:
		evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER);
		evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION);
		evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE);
		evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND);
		evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE);
		evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT);
		evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW);
		evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT);
		evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT);
		evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT);
		evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT);
		evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT);
		evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER);
		evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION);
		evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT);
		evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK);
		evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT);
		evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU);
		evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT);
		evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR);
		evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW);
		evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE);
		evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT);
		evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT);
		evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW);
		evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW);
		evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME);
		evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT);
		return GF_OK;
	}
	return GF_OK;
}
