blob: debe8dd30cd767c0b58ea86cdc29e5eddcb91545 [file] [log] [blame]
Benny Prijonoff64ccf2009-07-16 11:37:15 +00001/* $Id$ */
2/*
Nanang Izzuddina62ffc92011-05-05 06:14:19 +00003 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
Benny Prijonoff64ccf2009-07-16 11:37:15 +00004 *
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
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono7b40c6c2009-07-16 10:36:48 +000019#include "gui.h"
20#include "systest.h"
21#include <windows.h>
22
23
24#include "gui.h"
25#include <pjlib.h>
26#include <windows.h>
27#include <winuserm.h>
28#include <aygshell.h>
29
30#define MAINWINDOWCLASS TEXT("SysTestDlg")
Benny Prijonoff64ccf2009-07-16 11:37:15 +000031#define MAINWINDOWTITLE TEXT("PJSYSTEST")
Benny Prijono7b40c6c2009-07-16 10:36:48 +000032
33typedef struct menu_handler_t {
34 UINT id;
35 gui_menu_handler handler;
36} menu_handler_t;
37
38static HINSTANCE g_hInst;
39static HWND g_hWndMenuBar;
40static HWND g_hWndMain;
41static HWND g_hWndLog;
42static pj_thread_t *g_log_thread;
43static gui_menu *g_menu;
44static unsigned g_menu_handler_cnt;
45static menu_handler_t g_menu_handlers[64];
46
47static pj_log_func *g_log_writer_orig;
48
49static pj_status_t gui_update_menu(gui_menu *menu);
50
51static void log_writer(int level, const char *buffer, int len)
52{
53 wchar_t buf[512];
54 int cur_len;
55
56 PJ_UNUSED_ARG(level);
57
58 pj_ansi_to_unicode(buffer, len, buf, 512);
59
60 if (!g_hWndLog)
61 return;
62
63 /* For now, ignore log messages from other thread to avoid deadlock */
64 if (g_log_thread == pj_thread_this()) {
65 cur_len = (int)SendMessage(g_hWndLog, WM_GETTEXTLENGTH, 0, 0);
66 SendMessage(g_hWndLog, EM_SETSEL, (WPARAM)cur_len, (LPARAM)cur_len);
67 SendMessage(g_hWndLog, EM_REPLACESEL, (WPARAM)0, (LPARAM)buf);
68 }
69
70 //uncomment to forward to the original log writer
71 if (g_log_writer_orig)
72 (*g_log_writer_orig)(level, buffer, len);
73}
74
75/* execute menu handler for id menu specified, return FALSE if menu handler
76 * is not found.
77 */
78static BOOL handle_menu(UINT id)
79{
80 unsigned i;
81
82 for (i = 0; i < g_menu_handler_cnt; ++i) {
83 if (g_menu_handlers[i].id == id) {
84 /* menu handler found, execute it */
85 (*g_menu_handlers[i].handler)();
86 return TRUE;
87 }
88 }
89
90 return FALSE;
91}
92
93/* generate submenu and register the menu handler, then return next menu id */
94static UINT generate_submenu(HMENU parent, UINT id_start, gui_menu *menu)
95{
96 unsigned i;
97 UINT id = id_start;
98
99 if (!menu)
100 return id;
101
102 /* generate submenu */
103 for (i = 0; i < menu->submenu_cnt; ++i) {
104
105 if (menu->submenus[i] == NULL) {
106
107 /* add separator */
108 AppendMenu(parent, MF_SEPARATOR, 0, 0);
109
110 } else if (menu->submenus[i]->submenu_cnt != 0) {
111
112 /* this submenu item has children, generate popup menu */
113 HMENU hMenu;
114 wchar_t buf[64];
115
116 pj_ansi_to_unicode(menu->submenus[i]->title,
117 pj_ansi_strlen(menu->submenus[i]->title),
118 buf, 64);
119
120 hMenu = CreatePopupMenu();
121 AppendMenu(parent, MF_STRING|MF_ENABLED|MF_POPUP, (UINT)hMenu, buf);
122 id = generate_submenu(hMenu, id, menu->submenus[i]);
123
124 } else {
125
126 /* this submenu item is leaf, register the handler */
127 wchar_t buf[64];
128
129 pj_ansi_to_unicode(menu->submenus[i]->title,
130 pj_ansi_strlen(menu->submenus[i]->title),
131 buf, 64);
132
133 AppendMenu(parent, MF_STRING, id, buf);
134
135 if (menu->submenus[i]->handler) {
136 g_menu_handlers[g_menu_handler_cnt].id = id;
137 g_menu_handlers[g_menu_handler_cnt].handler =
138 menu->submenus[i]->handler;
139 ++g_menu_handler_cnt;
140 }
141
142 ++id;
143 }
144 }
145
146 return id;
147}
148
149BOOL InitDialog()
150{
151 /* update menu */
152 if (gui_update_menu(g_menu) != PJ_SUCCESS)
153 return FALSE;
154
155 return TRUE;
156}
157
158LRESULT CALLBACK DialogProc(const HWND hWnd,
159 const UINT Msg,
160 const WPARAM wParam,
161 const LPARAM lParam)
162{
163 LRESULT res = 0;
164
165 switch (Msg) {
166 case WM_CREATE:
167 g_hWndMain = hWnd;
168 if (FALSE == InitDialog()){
169 DestroyWindow(g_hWndMain);
170 }
171 break;
172
173 case WM_CLOSE:
174 DestroyWindow(g_hWndMain);
175 break;
176
177 case WM_DESTROY:
178 if (g_hWndMenuBar)
179 DestroyWindow(g_hWndMenuBar);
180 g_hWndMenuBar = NULL;
181 g_hWndMain = NULL;
182 PostQuitMessage(0);
183 break;
184
185 case WM_HOTKEY:
186 /* Exit app when back is pressed. */
187 if (VK_TBACK == HIWORD(lParam) && (0 != (MOD_KEYUP & LOWORD(lParam)))) {
188 DestroyWindow(g_hWndMain);
189 } else {
190 return DefWindowProc(hWnd, Msg, wParam, lParam);
191 }
192 break;
193
194 case WM_COMMAND:
195 res = handle_menu(LOWORD(wParam));
196 break;
197
198 default:
199 return DefWindowProc(hWnd, Msg, wParam, lParam);
200 }
201
202 return res;
203}
204
205
206/* === API === */
207
Benny Prijonoff64ccf2009-07-16 11:37:15 +0000208pj_status_t gui_init(gui_menu *menu)
Benny Prijono7b40c6c2009-07-16 10:36:48 +0000209{
210 WNDCLASS wc;
211 HWND hWnd = NULL;
212 RECT r;
213 DWORD dwStyle;
Benny Prijono7b40c6c2009-07-16 10:36:48 +0000214
215 pj_status_t status = PJ_SUCCESS;
216
217 /* Check if app is running. If it's running then focus on the window */
218 hWnd = FindWindow(MAINWINDOWCLASS, MAINWINDOWTITLE);
219
220 if (NULL != hWnd) {
221 SetForegroundWindow(hWnd);
222 return status;
223 }
224
225 g_menu = menu;
226
227 wc.style = CS_HREDRAW | CS_VREDRAW;
228 wc.lpfnWndProc = (WNDPROC)DialogProc;
229 wc.cbClsExtra = 0;
230 wc.cbWndExtra = 0;
231 wc.hInstance = g_hInst;
232 wc.hIcon = 0;
233 wc.hCursor = 0;
234 wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
235 wc.lpszMenuName = 0;
236 wc.lpszClassName = MAINWINDOWCLASS;
237
238 if (!RegisterClass(&wc) != 0) {
239 DWORD err = GetLastError();
Benny Prijono29580612010-08-11 06:03:47 +0000240 return PJ_RETURN_OS_ERROR(err);
Benny Prijono7b40c6c2009-07-16 10:36:48 +0000241 }
242
243 /* Create the app. window */
244 g_hWndMain = CreateWindow(MAINWINDOWCLASS, MAINWINDOWTITLE,
245 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
246 CW_USEDEFAULT, CW_USEDEFAULT,
247 (HWND)NULL, NULL, g_hInst, (LPSTR)NULL);
248
249 /* Create edit control to print log */
250 GetClientRect(g_hWndMain, &r);
251 dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL |
252 ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | ES_LEFT;
253 g_hWndLog = CreateWindow(
254 TEXT("EDIT"), // Class name
255 NULL, // Window text
256 dwStyle, // Window style
257 0, // x-coordinate of the upper-left corner
258 0, // y-coordinate of the upper-left corner
259 r.right-r.left, // Width of the window for the edit
260 // control
261 r.bottom-r.top, // Height of the window for the edit
262 // control
263 g_hWndMain, // Window handle to the parent window
264 (HMENU) 0, // Control identifier
265 g_hInst, // Instance handle
266 NULL); // Specify NULL for this parameter when
267 // you create a control
268
269 /* Resize the log */
270 if (g_hWndMenuBar) {
271 RECT r_menu = {0};
272
273 GetWindowRect(g_hWndLog, &r);
274 GetWindowRect(g_hWndMenuBar, &r_menu);
275 if (r.bottom > r_menu.top) {
276 MoveWindow(g_hWndLog, 0, 0, r.right-r.left,
277 (r.bottom-r.top)-(r_menu.bottom-r_menu.top), TRUE);
278 }
279 }
280
281 /* Focus it, so SP user can scroll the log */
282 SetFocus(g_hWndLog);
283
284 /* Get the log thread */
285 g_log_thread = pj_thread_this();
286
287 /* Redirect log & update log decor setting */
288 /*
289 log_decor = pj_log_get_decor();
290 log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR;
291 pj_log_set_decor(log_decor);
292 */
293 g_log_writer_orig = pj_log_get_log_func();
294 pj_log_set_log_func(&log_writer);
295
296 return status;
297}
298
299static pj_status_t gui_update_menu(gui_menu *menu)
300{
301 enum { MENU_ID_START = 50000 };
302 UINT id_start = MENU_ID_START;
303 HMENU hRootMenu;
304 SHMENUBARINFO mbi;
305
306 /* delete existing menu */
307 if (g_hWndMenuBar) {
308 DestroyWindow(g_hWndMenuBar);
309 g_hWndMenuBar = NULL;
310 }
311
312 /* delete menu handler map */
313 g_menu_handler_cnt = 0;
314
315 /* smartphone can only have two root menus */
316 pj_assert(menu->submenu_cnt <= 2);
317
318 /* generate menu tree */
319 hRootMenu = CreateMenu();
320 id_start = generate_submenu(hRootMenu, id_start, menu);
321
322 /* initialize menubar */
323 ZeroMemory(&mbi, sizeof(SHMENUBARINFO));
324 mbi.cbSize = sizeof(SHMENUBARINFO);
325 mbi.hwndParent = g_hWndMain;
326 mbi.dwFlags = SHCMBF_HIDESIPBUTTON|SHCMBF_HMENU;
327 mbi.nToolBarId = (UINT)hRootMenu;
328 mbi.hInstRes = g_hInst;
329
330 if (FALSE == SHCreateMenuBar(&mbi)) {
331 DWORD err = GetLastError();
332 return PJ_RETURN_OS_ERROR(err);
333 }
334
335 /* store menu window handle */
336 g_hWndMenuBar = mbi.hwndMB;
337
338 /* store current menu */
339 g_menu = menu;
340
341 /* show the menu */
342 DrawMenuBar(g_hWndMain);
343 ShowWindow(g_hWndMenuBar, SW_SHOW);
344
345 /* override back button */
346 SendMessage(g_hWndMenuBar, SHCMBM_OVERRIDEKEY, VK_TBACK,
347 MAKELPARAM(SHMBOF_NODEFAULT | SHMBOF_NOTIFY,
348 SHMBOF_NODEFAULT | SHMBOF_NOTIFY));
349
350
351 return PJ_SUCCESS;
352}
353
Benny Prijonoff64ccf2009-07-16 11:37:15 +0000354enum gui_key gui_msgbox(const char *title, const char *message, enum gui_flag flag)
Benny Prijono7b40c6c2009-07-16 10:36:48 +0000355{
356 wchar_t buf_title[64];
357 wchar_t buf_msg[512];
358 UINT wflag = 0;
359 int retcode;
360
361 pj_ansi_to_unicode(title, pj_ansi_strlen(title), buf_title, 64);
362 pj_ansi_to_unicode(message, pj_ansi_strlen(message), buf_msg, 512);
363
364 switch (flag) {
365 case WITH_OK:
366 wflag = MB_OK;
367 break;
368 case WITH_YESNO:
369 wflag = MB_YESNO;
370 break;
371 case WITH_OKCANCEL:
372 wflag = MB_OKCANCEL;
373 break;
374 }
375
376 retcode = MessageBox(g_hWndMain, buf_msg, buf_title, wflag);
377
378 switch (retcode) {
379 case IDOK:
380 return KEY_OK;
381 case IDYES:
382 return KEY_YES;
383 case IDNO:
384 return KEY_NO;
385 default:
386 return KEY_CANCEL;
387 }
388}
389
Benny Prijonoff64ccf2009-07-16 11:37:15 +0000390void gui_sleep(unsigned sec)
Benny Prijono7b40c6c2009-07-16 10:36:48 +0000391{
392 pj_thread_sleep(sec * 1000);
393}
394
Benny Prijonoff64ccf2009-07-16 11:37:15 +0000395pj_status_t gui_start(gui_menu *menu)
Benny Prijono7b40c6c2009-07-16 10:36:48 +0000396{
397 MSG msg;
398
399 PJ_UNUSED_ARG(menu);
400
401 while (GetMessage(&msg, NULL, 0, 0)) {
402 TranslateMessage(&msg);
403 DispatchMessage(&msg);
404 }
405
406 return (msg.wParam);
407}
408
Benny Prijonoff64ccf2009-07-16 11:37:15 +0000409void gui_destroy(void)
Benny Prijono7b40c6c2009-07-16 10:36:48 +0000410{
411 if (g_hWndMain) {
412 DestroyWindow(g_hWndMain);
413 g_hWndMain = NULL;
414 }
415}
416
417
418int WINAPI WinMain(
419 HINSTANCE hInstance,
420 HINSTANCE hPrevInstance,
421 LPWSTR lpCmdLine,
422 int nShowCmd
423)
424{
425 int status;
426
427 PJ_UNUSED_ARG(hPrevInstance);
428 PJ_UNUSED_ARG(lpCmdLine);
429 PJ_UNUSED_ARG(nShowCmd);
430
431 // store the hInstance in global
432 g_hInst = hInstance;
433
434 status = systest_init();
435 if (status != 0)
436 goto on_return;
437
438 status = systest_run();
439
440on_return:
441 systest_deinit();
442
443 return status;
444}