diff options
Diffstat (limited to 'pjsip-apps/src/pjsua_wince/pjsua_wince.cpp')
-rw-r--r-- | pjsip-apps/src/pjsua_wince/pjsua_wince.cpp | 780 |
1 files changed, 780 insertions, 0 deletions
diff --git a/pjsip-apps/src/pjsua_wince/pjsua_wince.cpp b/pjsip-apps/src/pjsua_wince/pjsua_wince.cpp new file mode 100644 index 0000000..a10c67f --- /dev/null +++ b/pjsip-apps/src/pjsua_wince/pjsua_wince.cpp @@ -0,0 +1,780 @@ +// pjsua_wince.cpp : Defines the entry point for the application. +// + +#include "stdafx.h" +#include "pjsua_wince.h" +#include <commctrl.h> +#include <pjsua-lib/pjsua.h> + +#define MAX_LOADSTRING 100 + +// Global Variables: +static HINSTANCE hInst; +static HWND hMainWnd; +static HWND hwndCB; +static HWND hwndGlobalStatus, hwndURI, hwndCallStatus; +static HWND hwndActionButton, hwndExitButton; + + + +// +// Basic config. +// +#define SIP_PORT 5060 + + +// +// Destination URI (to make call, or to subscribe presence) +// +#define SIP_DST_URI "sip:192.168.0.7:5061" + +// +// Account +// +#define HAS_SIP_ACCOUNT 0 // 0 to disable registration +#define SIP_DOMAIN "server" +#define SIP_REALM "server" +#define SIP_USER "user" +#define SIP_PASSWD "secret" + +// +// Outbound proxy for all accounts +// +#define SIP_PROXY NULL +//#define SIP_PROXY "sip:192.168.0.2;lr" + + +// +// Configure nameserver if DNS SRV is to be used with both SIP +// or STUN (for STUN see other settings below) +// +#define NAMESERVER NULL +//#define NAMESERVER "62.241.163.201" + +// +// STUN server +#if 0 + // Use this to have the STUN server resolved normally +# define STUN_DOMAIN NULL +# define STUN_SERVER "stun.fwdnet.net" +#elif 0 + // Use this to have the STUN server resolved with DNS SRV +# define STUN_DOMAIN "iptel.org" +# define STUN_SERVER NULL +#else + // Use this to disable STUN +# define STUN_DOMAIN NULL +# define STUN_SERVER NULL +#endif + +// +// Use ICE? +// +#define USE_ICE 0 + + +// +// Globals +// +static pj_pool_t *g_pool; +static pj_str_t g_local_uri; +static int g_current_acc; +static int g_current_call = PJSUA_INVALID_ID; +static int g_current_action; + +enum +{ + ID_GLOBAL_STATUS = 21, + ID_URI, + ID_CALL_STATUS, + ID_POLL_TIMER, +}; + +enum +{ + ID_MENU_NONE = 64, + ID_MENU_CALL, + ID_MENU_ANSWER, + ID_MENU_DISCONNECT, + ID_BTN_ACTION, +}; + + +// Forward declarations of functions included in this code module: +static ATOM MyRegisterClass (HINSTANCE, LPTSTR); +BOOL InitInstance (HINSTANCE, int); +static void OnCreate (HWND hWnd); +static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); + + + +///////////////////////////////////////////////////////////////////////////// + +static void OnError(const wchar_t *title, pj_status_t status) +{ + char errmsg[PJ_ERR_MSG_SIZE]; + PJ_DECL_UNICODE_TEMP_BUF(werrmsg, PJ_ERR_MSG_SIZE); + + pj_strerror(status, errmsg, sizeof(errmsg)); + + MessageBox(NULL, PJ_STRING_TO_NATIVE(errmsg, werrmsg, PJ_ERR_MSG_SIZE), + title, MB_OK); +} + + +static void SetLocalURI(const char *uri, int len, bool enabled=true) +{ + wchar_t tmp[128]; + if (len==-1) len=pj_ansi_strlen(uri); + pj_ansi_to_unicode(uri, len, tmp, PJ_ARRAY_SIZE(tmp)); + SetDlgItemText(hMainWnd, ID_GLOBAL_STATUS, tmp); + EnableWindow(hwndGlobalStatus, enabled?TRUE:FALSE); +} + + + +static void SetURI(const char *uri, int len, bool enabled=true) +{ + wchar_t tmp[128]; + if (len==-1) len=pj_ansi_strlen(uri); + pj_ansi_to_unicode(uri, len, tmp, PJ_ARRAY_SIZE(tmp)); + SetDlgItemText(hMainWnd, ID_URI, tmp); + EnableWindow(hwndURI, enabled?TRUE:FALSE); +} + + +static void SetCallStatus(const char *state, int len) +{ + wchar_t tmp[128]; + if (len==-1) len=pj_ansi_strlen(state); + pj_ansi_to_unicode(state, len, tmp, PJ_ARRAY_SIZE(tmp)); + SetDlgItemText(hMainWnd, ID_CALL_STATUS, tmp); +} + +static void SetAction(int action, bool enable=true) +{ + HMENU hMenu; + + hMenu = CommandBar_GetMenu(hwndCB, 0); + + RemoveMenu(hMenu, ID_MENU_NONE, MF_BYCOMMAND); + RemoveMenu(hMenu, ID_MENU_CALL, MF_BYCOMMAND); + RemoveMenu(hMenu, ID_MENU_ANSWER, MF_BYCOMMAND); + RemoveMenu(hMenu, ID_MENU_DISCONNECT, MF_BYCOMMAND); + + switch (action) { + case ID_MENU_NONE: + InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("None")); + SetWindowText(hwndActionButton, TEXT("-")); + break; + case ID_MENU_CALL: + InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Call")); + SetWindowText(hwndActionButton, TEXT("&Call")); + break; + case ID_MENU_ANSWER: + InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Answer")); + SetWindowText(hwndActionButton, TEXT("&Answer")); + break; + case ID_MENU_DISCONNECT: + InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Hangup")); + SetWindowText(hwndActionButton, TEXT("&Hangup")); + break; + } + + EnableMenuItem(hMenu, action, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED)); + DrawMenuBar(hMainWnd); + + g_current_action = action; +} + + +/* + * Handler when invite state has changed. + */ +static void on_call_state(pjsua_call_id call_id, pjsip_event *e) +{ + pjsua_call_info call_info; + + PJ_UNUSED_ARG(e); + + pjsua_call_get_info(call_id, &call_info); + + if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) { + + g_current_call = PJSUA_INVALID_ID; + SetURI(SIP_DST_URI, -1); + SetAction(ID_MENU_CALL); + //SetCallStatus(call_info.state_text.ptr, call_info.state_text.slen); + SetCallStatus(call_info.last_status_text.ptr, call_info.last_status_text.slen); + + } else { + //if (g_current_call == PJSUA_INVALID_ID) + // g_current_call = call_id; + + if (call_info.remote_contact.slen) + SetURI(call_info.remote_contact.ptr, call_info.remote_contact.slen, false); + else + SetURI(call_info.remote_info.ptr, call_info.remote_info.slen, false); + + if (call_info.state == PJSIP_INV_STATE_CONFIRMED) + SetAction(ID_MENU_DISCONNECT); + + SetCallStatus(call_info.state_text.ptr, call_info.state_text.slen); + } +} + + +/* + * Callback on media state changed event. + * The action may connect the call to sound device, to file, or + * to loop the call. + */ +static void on_call_media_state(pjsua_call_id call_id) +{ + pjsua_call_info call_info; + + pjsua_call_get_info(call_id, &call_info); + + if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) { + pjsua_conf_connect(call_info.conf_slot, 0); + pjsua_conf_connect(0, call_info.conf_slot); + } +} + + +/** + * Handler when there is incoming call. + */ +static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, + pjsip_rx_data *rdata) +{ + pjsua_call_info call_info; + + PJ_UNUSED_ARG(acc_id); + PJ_UNUSED_ARG(rdata); + + if (g_current_call != PJSUA_INVALID_ID) { + pj_str_t reason; + reason = pj_str("Another call is in progress"); + pjsua_call_answer(call_id, PJSIP_SC_BUSY_HERE, &reason, NULL); + return; + } + + g_current_call = call_id; + + pjsua_call_get_info(call_id, &call_info); + + SetAction(ID_MENU_ANSWER); + SetURI(call_info.remote_info.ptr, call_info.remote_info.slen, false); + pjsua_call_answer(call_id, 200, NULL, NULL); +} + + +/* + * Handler registration status has changed. + */ +static void on_reg_state(pjsua_acc_id acc_id) +{ + PJ_UNUSED_ARG(acc_id); + + // Log already written. +} + + +/* + * Handler on buddy state changed. + */ +static void on_buddy_state(pjsua_buddy_id buddy_id) +{ + /* Currently this is not processed */ + PJ_UNUSED_ARG(buddy_id); +} + + +/** + * Incoming IM message (i.e. MESSAGE request)! + */ +static void on_pager(pjsua_call_id call_id, const pj_str_t *from, + const pj_str_t *to, const pj_str_t *contact, + const pj_str_t *mime_type, const pj_str_t *text) +{ + /* Currently this is not processed */ + PJ_UNUSED_ARG(call_id); + PJ_UNUSED_ARG(from); + PJ_UNUSED_ARG(to); + PJ_UNUSED_ARG(contact); + PJ_UNUSED_ARG(mime_type); + PJ_UNUSED_ARG(text); +} + + +/** + * Received typing indication + */ +static void on_typing(pjsua_call_id call_id, const pj_str_t *from, + const pj_str_t *to, const pj_str_t *contact, + pj_bool_t is_typing) +{ + /* Currently this is not processed */ + PJ_UNUSED_ARG(call_id); + PJ_UNUSED_ARG(from); + PJ_UNUSED_ARG(to); + PJ_UNUSED_ARG(contact); + PJ_UNUSED_ARG(is_typing); +} + +/** + * Callback upon NAT detection completion + */ +static void nat_detect_cb(const pj_stun_nat_detect_result *res) +{ + if (res->status != PJ_SUCCESS) { + char msg[250]; + pj_ansi_snprintf(msg, sizeof(msg), "NAT detection failed: %s", + res->status_text); + SetCallStatus(msg, pj_ansi_strlen(msg)); + } else { + char msg[250]; + pj_ansi_snprintf(msg, sizeof(msg), "NAT type is %s", + res->nat_type_name); + SetCallStatus(msg, pj_ansi_strlen(msg)); + } +} + + +static BOOL OnInitStack(void) +{ + pjsua_config cfg; + pjsua_logging_config log_cfg; + pjsua_media_config media_cfg; + pjsua_transport_config udp_cfg; + pjsua_transport_config rtp_cfg; + pjsua_transport_id transport_id; + pjsua_transport_info transport_info; + pj_str_t tmp; + pj_status_t status; + + /* Create pjsua */ + status = pjsua_create(); + if (status != PJ_SUCCESS) { + OnError(TEXT("Error creating pjsua"), status); + return FALSE; + } + + /* Create global pool for application */ + g_pool = pjsua_pool_create("pjsua", 4000, 4000); + + /* Init configs */ + pjsua_config_default(&cfg); + pjsua_media_config_default(&media_cfg); + pjsua_transport_config_default(&udp_cfg); + udp_cfg.port = SIP_PORT; + + pjsua_transport_config_default(&rtp_cfg); + rtp_cfg.port = 40000; + + pjsua_logging_config_default(&log_cfg); + log_cfg.level = 5; + log_cfg.log_filename = pj_str("\\pjsua.txt"); + log_cfg.msg_logging = 1; + log_cfg.decor = pj_log_get_decor() | PJ_LOG_HAS_CR; + + /* Setup media */ + media_cfg.clock_rate = 8000; + media_cfg.ec_options = PJMEDIA_ECHO_SIMPLE; + media_cfg.ec_tail_len = 256; + // use default quality setting + //media_cfg.quality = 1; + media_cfg.ptime = 20; + media_cfg.enable_ice = USE_ICE; + + /* Initialize application callbacks */ + cfg.cb.on_call_state = &on_call_state; + cfg.cb.on_call_media_state = &on_call_media_state; + cfg.cb.on_incoming_call = &on_incoming_call; + cfg.cb.on_reg_state = &on_reg_state; + cfg.cb.on_buddy_state = &on_buddy_state; + cfg.cb.on_pager = &on_pager; + cfg.cb.on_typing = &on_typing; + cfg.cb.on_nat_detect = &nat_detect_cb; + + if (SIP_PROXY) { + cfg.outbound_proxy_cnt = 1; + cfg.outbound_proxy[0] = pj_str(SIP_PROXY); + } + + if (NAMESERVER) { + cfg.nameserver_count = 1; + cfg.nameserver[0] = pj_str(NAMESERVER); + } + + if (NAMESERVER && STUN_DOMAIN) { + cfg.stun_domain = pj_str(STUN_DOMAIN); + } else if (STUN_SERVER) { + cfg.stun_host = pj_str(STUN_SERVER); + } + + + /* Initialize pjsua */ + status = pjsua_init(&cfg, &log_cfg, &media_cfg); + if (status != PJ_SUCCESS) { + OnError(TEXT("Initialization error"), status); + return FALSE; + } + + /* Set codec priority */ + pjsua_codec_set_priority(pj_cstr(&tmp, "pcmu"), 240); + pjsua_codec_set_priority(pj_cstr(&tmp, "pcma"), 230); + pjsua_codec_set_priority(pj_cstr(&tmp, "speex/8000"), 190); + pjsua_codec_set_priority(pj_cstr(&tmp, "ilbc"), 189); + pjsua_codec_set_priority(pj_cstr(&tmp, "speex/16000"), 180); + pjsua_codec_set_priority(pj_cstr(&tmp, "speex/32000"), 0); + pjsua_codec_set_priority(pj_cstr(&tmp, "gsm"), 100); + + + /* Add UDP transport and the corresponding PJSUA account */ + status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, + &udp_cfg, &transport_id); + if (status != PJ_SUCCESS) { + OnError(TEXT("Error starting SIP transport"), status); + return FALSE; + } + + pjsua_transport_get_info(transport_id, &transport_info); + + g_local_uri.ptr = (char*)pj_pool_alloc(g_pool, 128); + g_local_uri.slen = pj_ansi_sprintf(g_local_uri.ptr, + "<sip:%.*s:%d>", + (int)transport_info.local_name.host.slen, + transport_info.local_name.host.ptr, + transport_info.local_name.port); + + + /* Add local account */ + pjsua_acc_add_local(transport_id, PJ_TRUE, &g_current_acc); + pjsua_acc_set_online_status(g_current_acc, PJ_TRUE); + + /* Add account */ + if (HAS_SIP_ACCOUNT) { + pjsua_acc_config cfg; + + pjsua_acc_config_default(&cfg); + cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN); + cfg.reg_uri = pj_str("sip:" SIP_DOMAIN); + cfg.cred_count = 1; + cfg.cred_info[0].realm = pj_str(SIP_REALM); + cfg.cred_info[0].scheme = pj_str("digest"); + cfg.cred_info[0].username = pj_str(SIP_USER); + cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + cfg.cred_info[0].data = pj_str(SIP_PASSWD); + + status = pjsua_acc_add(&cfg, PJ_TRUE, &g_current_acc); + if (status != PJ_SUCCESS) { + pjsua_destroy(); + return PJ_FALSE; + } + } + + /* Add buddy */ + if (SIP_DST_URI) { + pjsua_buddy_config bcfg; + + pjsua_buddy_config_default(&bcfg); + bcfg.uri = pj_str(SIP_DST_URI); + bcfg.subscribe = PJ_FALSE; + + pjsua_buddy_add(&bcfg, NULL); + } + + /* Start pjsua */ + status = pjsua_start(); + if (status != PJ_SUCCESS) { + OnError(TEXT("Error starting pjsua"), status); + return FALSE; + } + + return TRUE; +} + + +////////////////////////////////////////////////////////////////////////////// + +int WINAPI WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + MSG msg; + HACCEL hAccelTable; + + PJ_UNUSED_ARG(lpCmdLine); + PJ_UNUSED_ARG(hPrevInstance); + + // Perform application initialization: + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_PJSUA_WINCE); + + + // Main message loop: + while (GetMessage(&msg, NULL, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return msg.wParam; +} + +static ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass) +{ + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = (WNDPROC) WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PJSUA_WINCE)); + wc.hCursor = 0; + wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); + wc.lpszMenuName = 0; + wc.lpszClassName = szWindowClass; + + return RegisterClass(&wc); +} + + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + HWND hWnd; + TCHAR szTitle[MAX_LOADSTRING]; + TCHAR szWindowClass[MAX_LOADSTRING]; + + hInst = hInstance; + + /* Init stack */ + if (OnInitStack() == FALSE) + return FALSE; + + LoadString(hInstance, IDC_PJSUA_WINCE, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance, szWindowClass); + + LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 200, + NULL, NULL, hInstance, NULL); + + if (!hWnd) + { + return FALSE; + } + + hMainWnd = hWnd; + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + if (hwndCB) + CommandBar_Show(hwndCB, TRUE); + + SetTimer(hMainWnd, ID_POLL_TIMER, 50, NULL); + + pjsua_detect_nat_type(); + return TRUE; +} + + +static void OnCreate(HWND hWnd) +{ + enum + { + X = 10, + Y = 40, + W = 220, + H = 30, + }; + + DWORD dwStyle; + + hMainWnd = hWnd; + + hwndCB = CommandBar_Create(hInst, hWnd, 1); + CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0); + CommandBar_AddAdornments(hwndCB, 0, 0); + + // Create global status text + dwStyle = WS_CHILD | WS_VISIBLE | WS_DISABLED | ES_LEFT; + hwndGlobalStatus = CreateWindow( + TEXT("EDIT"), // Class name + NULL, // Window text + dwStyle, // Window style + X, // x-coordinate of the upper-left corner + Y+0, // y-coordinate of the upper-left corner + W, // Width of the window for the edit + // control + H-5, // Height of the window for the edit + // control + hWnd, // Window handle to the parent window + (HMENU) ID_GLOBAL_STATUS, // Control identifier + hInst, // Instance handle + NULL); // Specify NULL for this parameter when + // you create a control + SetLocalURI(g_local_uri.ptr, g_local_uri.slen, false); + + + // Create URI edit + dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER; + hwndURI = CreateWindow ( + TEXT("EDIT"), // Class name + NULL, // Window text + dwStyle, // Window style + X, // x-coordinate of the upper-left corner + Y+H, // y-coordinate of the upper-left corner + W, // Width of the window for the edit + // control + H-5, // Height of the window for the edit + // control + hWnd, // Window handle to the parent window + (HMENU) ID_URI, // Control identifier + hInst, // Instance handle + NULL); // Specify NULL for this parameter when + // you create a control + + // Create action Button + hwndActionButton = CreateWindow( L"button", L"Action", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + X, Y+2*H, + 60, H-5, hWnd, + (HMENU) ID_BTN_ACTION, + hInst, NULL ); + + // Create exit button + hwndExitButton = CreateWindow( L"button", L"E&xit", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + X+70, Y+2*H, + 60, H-5, hWnd, + (HMENU) ID_EXIT, + hInst, NULL ); + + + // Create call status edit + dwStyle = WS_CHILD | WS_VISIBLE | WS_DISABLED; + hwndCallStatus = CreateWindow ( + TEXT("EDIT"), // Class name + NULL, // Window text + dwStyle, // Window style + X, // x-coordinate of the upper-left corner + Y+3*H, // y-coordinate of the upper-left corner + W, // Width of the window for the edit + // control + H-5, // Height of the window for the edit + // control + hWnd, // Window handle to the parent window + (HMENU) ID_CALL_STATUS, // Control identifier + hInst, // Instance handle + NULL); // Specify NULL for this parameter when + // you create a control + SetCallStatus("Ready", 5); + SetAction(ID_MENU_CALL); + SetURI(SIP_DST_URI, -1); + SetFocus(hWnd); + +} + + +static void OnDestroy(void) +{ + pjsua_destroy(); +} + +static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + + switch (message) { + case WM_KEYUP: + if (wParam==114) { + wParam = ID_MENU_CALL; + } else if (wParam==115) { + if (g_current_call == PJSUA_INVALID_ID) + wParam = ID_EXIT; + else + wParam = ID_MENU_DISCONNECT; + } else + break; + + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + if (wmId == ID_BTN_ACTION) + wmId = g_current_action; + switch (wmId) + { + case ID_MENU_CALL: + if (g_current_call != PJSUA_INVALID_ID) { + MessageBox(NULL, TEXT("Can not make call"), + TEXT("You already have one call active"), MB_OK); + } + pj_str_t dst_uri; + wchar_t text[256]; + char tmp[256]; + pj_status_t status; + + GetWindowText(hwndURI, text, PJ_ARRAY_SIZE(text)); + pj_unicode_to_ansi(text, pj_unicode_strlen(text), + tmp, sizeof(tmp)); + dst_uri.ptr = tmp; + dst_uri.slen = pj_ansi_strlen(tmp); + status = pjsua_call_make_call(g_current_acc, + &dst_uri, 0, NULL, + NULL, &g_current_call); + if (status != PJ_SUCCESS) + OnError(TEXT("Unable to make call"), status); + break; + case ID_MENU_ANSWER: + if (g_current_call == PJSUA_INVALID_ID) + MessageBox(NULL, TEXT("Can not answer"), + TEXT("There is no call!"), MB_OK); + else + pjsua_call_answer(g_current_call, 200, NULL, NULL); + break; + case ID_MENU_DISCONNECT: + if (g_current_call == PJSUA_INVALID_ID) + MessageBox(NULL, TEXT("Can not disconnect"), + TEXT("There is no call!"), MB_OK); + else + pjsua_call_hangup(g_current_call, PJSIP_SC_DECLINE, NULL, NULL); + break; + case ID_EXIT: + DestroyWindow(hWnd); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + case WM_CREATE: + OnCreate(hWnd); + break; + + case WM_DESTROY: + OnDestroy(); + CommandBar_Destroy(hwndCB); + PostQuitMessage(0); + break; + + case WM_TIMER: + pjsua_handle_events(1); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + |