| /* $Id$ */ |
| /* |
| * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.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 |
| */ |
| #include "gui.h" |
| #include "systest.h" |
| #include <windows.h> |
| |
| |
| #include "gui.h" |
| #include <pjlib.h> |
| #include <windows.h> |
| #include <winuserm.h> |
| #include <aygshell.h> |
| |
| #define MAINWINDOWCLASS TEXT("SysTestDlg") |
| #define MAINWINDOWTITLE TEXT("PJSYSTEST") |
| |
| typedef struct menu_handler_t { |
| UINT id; |
| gui_menu_handler handler; |
| } menu_handler_t; |
| |
| static HINSTANCE g_hInst; |
| static HWND g_hWndMenuBar; |
| static HWND g_hWndMain; |
| static HWND g_hWndLog; |
| static pj_thread_t *g_log_thread; |
| static gui_menu *g_menu; |
| static unsigned g_menu_handler_cnt; |
| static menu_handler_t g_menu_handlers[64]; |
| |
| static pj_log_func *g_log_writer_orig; |
| |
| static pj_status_t gui_update_menu(gui_menu *menu); |
| |
| static void log_writer(int level, const char *buffer, int len) |
| { |
| wchar_t buf[512]; |
| int cur_len; |
| |
| PJ_UNUSED_ARG(level); |
| |
| pj_ansi_to_unicode(buffer, len, buf, 512); |
| |
| if (!g_hWndLog) |
| return; |
| |
| /* For now, ignore log messages from other thread to avoid deadlock */ |
| if (g_log_thread == pj_thread_this()) { |
| cur_len = (int)SendMessage(g_hWndLog, WM_GETTEXTLENGTH, 0, 0); |
| SendMessage(g_hWndLog, EM_SETSEL, (WPARAM)cur_len, (LPARAM)cur_len); |
| SendMessage(g_hWndLog, EM_REPLACESEL, (WPARAM)0, (LPARAM)buf); |
| } |
| |
| //uncomment to forward to the original log writer |
| if (g_log_writer_orig) |
| (*g_log_writer_orig)(level, buffer, len); |
| } |
| |
| /* execute menu handler for id menu specified, return FALSE if menu handler |
| * is not found. |
| */ |
| static BOOL handle_menu(UINT id) |
| { |
| unsigned i; |
| |
| for (i = 0; i < g_menu_handler_cnt; ++i) { |
| if (g_menu_handlers[i].id == id) { |
| /* menu handler found, execute it */ |
| (*g_menu_handlers[i].handler)(); |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| /* generate submenu and register the menu handler, then return next menu id */ |
| static UINT generate_submenu(HMENU parent, UINT id_start, gui_menu *menu) |
| { |
| unsigned i; |
| UINT id = id_start; |
| |
| if (!menu) |
| return id; |
| |
| /* generate submenu */ |
| for (i = 0; i < menu->submenu_cnt; ++i) { |
| |
| if (menu->submenus[i] == NULL) { |
| |
| /* add separator */ |
| AppendMenu(parent, MF_SEPARATOR, 0, 0); |
| |
| } else if (menu->submenus[i]->submenu_cnt != 0) { |
| |
| /* this submenu item has children, generate popup menu */ |
| HMENU hMenu; |
| wchar_t buf[64]; |
| |
| pj_ansi_to_unicode(menu->submenus[i]->title, |
| pj_ansi_strlen(menu->submenus[i]->title), |
| buf, 64); |
| |
| hMenu = CreatePopupMenu(); |
| AppendMenu(parent, MF_STRING|MF_ENABLED|MF_POPUP, (UINT)hMenu, buf); |
| id = generate_submenu(hMenu, id, menu->submenus[i]); |
| |
| } else { |
| |
| /* this submenu item is leaf, register the handler */ |
| wchar_t buf[64]; |
| |
| pj_ansi_to_unicode(menu->submenus[i]->title, |
| pj_ansi_strlen(menu->submenus[i]->title), |
| buf, 64); |
| |
| AppendMenu(parent, MF_STRING, id, buf); |
| |
| if (menu->submenus[i]->handler) { |
| g_menu_handlers[g_menu_handler_cnt].id = id; |
| g_menu_handlers[g_menu_handler_cnt].handler = |
| menu->submenus[i]->handler; |
| ++g_menu_handler_cnt; |
| } |
| |
| ++id; |
| } |
| } |
| |
| return id; |
| } |
| |
| BOOL InitDialog() |
| { |
| /* update menu */ |
| if (gui_update_menu(g_menu) != PJ_SUCCESS) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| LRESULT CALLBACK DialogProc(const HWND hWnd, |
| const UINT Msg, |
| const WPARAM wParam, |
| const LPARAM lParam) |
| { |
| LRESULT res = 0; |
| |
| switch (Msg) { |
| case WM_CREATE: |
| g_hWndMain = hWnd; |
| if (FALSE == InitDialog()){ |
| DestroyWindow(g_hWndMain); |
| } |
| break; |
| |
| case WM_CLOSE: |
| DestroyWindow(g_hWndMain); |
| break; |
| |
| case WM_DESTROY: |
| if (g_hWndMenuBar) |
| DestroyWindow(g_hWndMenuBar); |
| g_hWndMenuBar = NULL; |
| g_hWndMain = NULL; |
| PostQuitMessage(0); |
| break; |
| |
| case WM_HOTKEY: |
| /* Exit app when back is pressed. */ |
| if (VK_TBACK == HIWORD(lParam) && (0 != (MOD_KEYUP & LOWORD(lParam)))) { |
| DestroyWindow(g_hWndMain); |
| } else { |
| return DefWindowProc(hWnd, Msg, wParam, lParam); |
| } |
| break; |
| |
| case WM_COMMAND: |
| res = handle_menu(LOWORD(wParam)); |
| break; |
| |
| default: |
| return DefWindowProc(hWnd, Msg, wParam, lParam); |
| } |
| |
| return res; |
| } |
| |
| |
| /* === API === */ |
| |
| pj_status_t gui_init(gui_menu *menu) |
| { |
| WNDCLASS wc; |
| HWND hWnd = NULL; |
| RECT r; |
| DWORD dwStyle; |
| |
| pj_status_t status = PJ_SUCCESS; |
| |
| /* Check if app is running. If it's running then focus on the window */ |
| hWnd = FindWindow(MAINWINDOWCLASS, MAINWINDOWTITLE); |
| |
| if (NULL != hWnd) { |
| SetForegroundWindow(hWnd); |
| return status; |
| } |
| |
| g_menu = menu; |
| |
| wc.style = CS_HREDRAW | CS_VREDRAW; |
| wc.lpfnWndProc = (WNDPROC)DialogProc; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = 0; |
| wc.hInstance = g_hInst; |
| wc.hIcon = 0; |
| wc.hCursor = 0; |
| wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); |
| wc.lpszMenuName = 0; |
| wc.lpszClassName = MAINWINDOWCLASS; |
| |
| if (!RegisterClass(&wc) != 0) { |
| DWORD err = GetLastError(); |
| return PJ_RETURN_OS_ERROR(err); |
| } |
| |
| /* Create the app. window */ |
| g_hWndMain = CreateWindow(MAINWINDOWCLASS, MAINWINDOWTITLE, |
| WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, |
| CW_USEDEFAULT, CW_USEDEFAULT, |
| (HWND)NULL, NULL, g_hInst, (LPSTR)NULL); |
| |
| /* Create edit control to print log */ |
| GetClientRect(g_hWndMain, &r); |
| dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL | |
| ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | ES_LEFT; |
| g_hWndLog = CreateWindow( |
| TEXT("EDIT"), // Class name |
| NULL, // Window text |
| dwStyle, // Window style |
| 0, // x-coordinate of the upper-left corner |
| 0, // y-coordinate of the upper-left corner |
| r.right-r.left, // Width of the window for the edit |
| // control |
| r.bottom-r.top, // Height of the window for the edit |
| // control |
| g_hWndMain, // Window handle to the parent window |
| (HMENU) 0, // Control identifier |
| g_hInst, // Instance handle |
| NULL); // Specify NULL for this parameter when |
| // you create a control |
| |
| /* Resize the log */ |
| if (g_hWndMenuBar) { |
| RECT r_menu = {0}; |
| |
| GetWindowRect(g_hWndLog, &r); |
| GetWindowRect(g_hWndMenuBar, &r_menu); |
| if (r.bottom > r_menu.top) { |
| MoveWindow(g_hWndLog, 0, 0, r.right-r.left, |
| (r.bottom-r.top)-(r_menu.bottom-r_menu.top), TRUE); |
| } |
| } |
| |
| /* Focus it, so SP user can scroll the log */ |
| SetFocus(g_hWndLog); |
| |
| /* Get the log thread */ |
| g_log_thread = pj_thread_this(); |
| |
| /* Redirect log & update log decor setting */ |
| /* |
| log_decor = pj_log_get_decor(); |
| log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR; |
| pj_log_set_decor(log_decor); |
| */ |
| g_log_writer_orig = pj_log_get_log_func(); |
| pj_log_set_log_func(&log_writer); |
| |
| return status; |
| } |
| |
| static pj_status_t gui_update_menu(gui_menu *menu) |
| { |
| enum { MENU_ID_START = 50000 }; |
| UINT id_start = MENU_ID_START; |
| HMENU hRootMenu; |
| SHMENUBARINFO mbi; |
| |
| /* delete existing menu */ |
| if (g_hWndMenuBar) { |
| DestroyWindow(g_hWndMenuBar); |
| g_hWndMenuBar = NULL; |
| } |
| |
| /* delete menu handler map */ |
| g_menu_handler_cnt = 0; |
| |
| /* smartphone can only have two root menus */ |
| pj_assert(menu->submenu_cnt <= 2); |
| |
| /* generate menu tree */ |
| hRootMenu = CreateMenu(); |
| id_start = generate_submenu(hRootMenu, id_start, menu); |
| |
| /* initialize menubar */ |
| ZeroMemory(&mbi, sizeof(SHMENUBARINFO)); |
| mbi.cbSize = sizeof(SHMENUBARINFO); |
| mbi.hwndParent = g_hWndMain; |
| mbi.dwFlags = SHCMBF_HIDESIPBUTTON|SHCMBF_HMENU; |
| mbi.nToolBarId = (UINT)hRootMenu; |
| mbi.hInstRes = g_hInst; |
| |
| if (FALSE == SHCreateMenuBar(&mbi)) { |
| DWORD err = GetLastError(); |
| return PJ_RETURN_OS_ERROR(err); |
| } |
| |
| /* store menu window handle */ |
| g_hWndMenuBar = mbi.hwndMB; |
| |
| /* store current menu */ |
| g_menu = menu; |
| |
| /* show the menu */ |
| DrawMenuBar(g_hWndMain); |
| ShowWindow(g_hWndMenuBar, SW_SHOW); |
| |
| /* override back button */ |
| SendMessage(g_hWndMenuBar, SHCMBM_OVERRIDEKEY, VK_TBACK, |
| MAKELPARAM(SHMBOF_NODEFAULT | SHMBOF_NOTIFY, |
| SHMBOF_NODEFAULT | SHMBOF_NOTIFY)); |
| |
| |
| return PJ_SUCCESS; |
| } |
| |
| enum gui_key gui_msgbox(const char *title, const char *message, enum gui_flag flag) |
| { |
| wchar_t buf_title[64]; |
| wchar_t buf_msg[512]; |
| UINT wflag = 0; |
| int retcode; |
| |
| pj_ansi_to_unicode(title, pj_ansi_strlen(title), buf_title, 64); |
| pj_ansi_to_unicode(message, pj_ansi_strlen(message), buf_msg, 512); |
| |
| switch (flag) { |
| case WITH_OK: |
| wflag = MB_OK; |
| break; |
| case WITH_YESNO: |
| wflag = MB_YESNO; |
| break; |
| case WITH_OKCANCEL: |
| wflag = MB_OKCANCEL; |
| break; |
| } |
| |
| retcode = MessageBox(g_hWndMain, buf_msg, buf_title, wflag); |
| |
| switch (retcode) { |
| case IDOK: |
| return KEY_OK; |
| case IDYES: |
| return KEY_YES; |
| case IDNO: |
| return KEY_NO; |
| default: |
| return KEY_CANCEL; |
| } |
| } |
| |
| void gui_sleep(unsigned sec) |
| { |
| pj_thread_sleep(sec * 1000); |
| } |
| |
| pj_status_t gui_start(gui_menu *menu) |
| { |
| MSG msg; |
| |
| PJ_UNUSED_ARG(menu); |
| |
| while (GetMessage(&msg, NULL, 0, 0)) { |
| TranslateMessage(&msg); |
| DispatchMessage(&msg); |
| } |
| |
| return (msg.wParam); |
| } |
| |
| void gui_destroy(void) |
| { |
| if (g_hWndMain) { |
| DestroyWindow(g_hWndMain); |
| g_hWndMain = NULL; |
| } |
| } |
| |
| |
| int WINAPI WinMain( |
| HINSTANCE hInstance, |
| HINSTANCE hPrevInstance, |
| LPWSTR lpCmdLine, |
| int nShowCmd |
| ) |
| { |
| int status; |
| |
| PJ_UNUSED_ARG(hPrevInstance); |
| PJ_UNUSED_ARG(lpCmdLine); |
| PJ_UNUSED_ARG(nShowCmd); |
| |
| // store the hInstance in global |
| g_hInst = hInstance; |
| |
| status = systest_init(); |
| if (status != 0) |
| goto on_return; |
| |
| status = systest_run(); |
| |
| on_return: |
| systest_deinit(); |
| |
| return status; |
| } |