Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjsip-apps / src / pjsua_wince / pjsua_wince.cpp
1 // pjsua_wince.cpp : Defines the entry point for the application.
2 //
3
4 #include "stdafx.h"
5 #include "pjsua_wince.h"
6 #include <commctrl.h>
7 #include <pjsua-lib/pjsua.h>
8
9 #define MAX_LOADSTRING 100
10
11 // Global Variables:
12 static HINSTANCE    hInst;
13 static HWND         hMainWnd;
14 static HWND         hwndCB;
15 static HWND         hwndGlobalStatus, hwndURI, hwndCallStatus;
16 static HWND         hwndActionButton, hwndExitButton;
17
18
19
20 //
21 // Basic config.
22 //
23 #define SIP_PORT        5060
24
25
26 //
27 // Destination URI (to make call, or to subscribe presence)
28 //
29 #define SIP_DST_URI     "sip:192.168.0.7:5061"
30
31 //
32 // Account
33 //
34 #define HAS_SIP_ACCOUNT 0       // 0 to disable registration
35 #define SIP_DOMAIN      "server"
36 #define SIP_REALM       "server"
37 #define SIP_USER        "user"
38 #define SIP_PASSWD      "secret"
39
40 //
41 // Outbound proxy for all accounts
42 //
43 #define SIP_PROXY       NULL
44 //#define SIP_PROXY     "sip:192.168.0.2;lr"
45
46
47 //
48 // Configure nameserver if DNS SRV is to be used with both SIP
49 // or STUN (for STUN see other settings below)
50 //
51 #define NAMESERVER      NULL
52 //#define NAMESERVER    "62.241.163.201"
53
54 //
55 // STUN server
56 #if 0
57         // Use this to have the STUN server resolved normally
58 #   define STUN_DOMAIN  NULL
59 #   define STUN_SERVER  "stun.fwdnet.net"
60 #elif 0
61         // Use this to have the STUN server resolved with DNS SRV
62 #   define STUN_DOMAIN  "iptel.org"
63 #   define STUN_SERVER  NULL
64 #else
65         // Use this to disable STUN
66 #   define STUN_DOMAIN  NULL
67 #   define STUN_SERVER  NULL
68 #endif
69
70 //
71 // Use ICE?
72 //
73 #define USE_ICE         0
74
75
76 //
77 // Globals
78 //
79 static pj_pool_t   *g_pool;
80 static pj_str_t     g_local_uri;
81 static int          g_current_acc;
82 static int          g_current_call = PJSUA_INVALID_ID;
83 static int          g_current_action;
84
85 enum
86 {
87     ID_GLOBAL_STATUS    = 21,
88     ID_URI,
89     ID_CALL_STATUS,
90     ID_POLL_TIMER,
91 };
92
93 enum
94 {
95     ID_MENU_NONE        = 64,
96     ID_MENU_CALL,
97     ID_MENU_ANSWER,
98     ID_MENU_DISCONNECT,
99     ID_BTN_ACTION,
100 };
101
102
103 // Forward declarations of functions included in this code module:
104 static ATOM             MyRegisterClass (HINSTANCE, LPTSTR);
105 BOOL                    InitInstance    (HINSTANCE, int);
106 static void             OnCreate        (HWND hWnd);
107 static LRESULT CALLBACK WndProc         (HWND, UINT, WPARAM, LPARAM);
108
109
110
111 /////////////////////////////////////////////////////////////////////////////
112
113 static void OnError(const wchar_t *title, pj_status_t status)
114 {
115     char errmsg[PJ_ERR_MSG_SIZE];
116     PJ_DECL_UNICODE_TEMP_BUF(werrmsg, PJ_ERR_MSG_SIZE);
117
118     pj_strerror(status, errmsg, sizeof(errmsg));
119     
120     MessageBox(NULL, PJ_STRING_TO_NATIVE(errmsg, werrmsg, PJ_ERR_MSG_SIZE),
121                title, MB_OK);
122 }
123
124
125 static void SetLocalURI(const char *uri, int len, bool enabled=true)
126 {
127     wchar_t tmp[128];
128     if (len==-1) len=pj_ansi_strlen(uri);
129     pj_ansi_to_unicode(uri, len, tmp, PJ_ARRAY_SIZE(tmp));
130     SetDlgItemText(hMainWnd, ID_GLOBAL_STATUS, tmp);
131     EnableWindow(hwndGlobalStatus, enabled?TRUE:FALSE);
132 }
133
134
135
136 static void SetURI(const char *uri, int len, bool enabled=true)
137 {
138     wchar_t tmp[128];
139     if (len==-1) len=pj_ansi_strlen(uri);
140     pj_ansi_to_unicode(uri, len, tmp, PJ_ARRAY_SIZE(tmp));
141     SetDlgItemText(hMainWnd, ID_URI, tmp);
142     EnableWindow(hwndURI, enabled?TRUE:FALSE);
143 }
144
145
146 static void SetCallStatus(const char *state, int len)
147 {
148     wchar_t tmp[128];
149     if (len==-1) len=pj_ansi_strlen(state);
150     pj_ansi_to_unicode(state, len, tmp, PJ_ARRAY_SIZE(tmp));
151     SetDlgItemText(hMainWnd, ID_CALL_STATUS, tmp);
152 }
153
154 static void SetAction(int action, bool enable=true)
155 {
156     HMENU hMenu;
157
158     hMenu = CommandBar_GetMenu(hwndCB, 0);
159
160     RemoveMenu(hMenu, ID_MENU_NONE, MF_BYCOMMAND);
161     RemoveMenu(hMenu, ID_MENU_CALL, MF_BYCOMMAND);
162     RemoveMenu(hMenu, ID_MENU_ANSWER, MF_BYCOMMAND);
163     RemoveMenu(hMenu, ID_MENU_DISCONNECT, MF_BYCOMMAND);
164
165     switch (action) {
166     case ID_MENU_NONE:
167         InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("None"));
168         SetWindowText(hwndActionButton, TEXT("-"));
169         break;
170     case ID_MENU_CALL:
171         InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Call"));
172         SetWindowText(hwndActionButton, TEXT("&Call"));
173         break;
174     case ID_MENU_ANSWER:
175         InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Answer"));
176         SetWindowText(hwndActionButton, TEXT("&Answer"));
177         break;
178     case ID_MENU_DISCONNECT:
179         InsertMenu(hMenu, ID_EXIT, MF_BYCOMMAND, action, TEXT("Hangup"));
180         SetWindowText(hwndActionButton, TEXT("&Hangup"));
181         break;
182     }
183
184     EnableMenuItem(hMenu, action, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED));
185     DrawMenuBar(hMainWnd);
186
187     g_current_action = action;
188 }
189
190
191 /*
192  * Handler when invite state has changed.
193  */
194 static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
195 {
196     pjsua_call_info call_info;
197
198     PJ_UNUSED_ARG(e);
199
200     pjsua_call_get_info(call_id, &call_info);
201
202     if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
203
204         g_current_call = PJSUA_INVALID_ID;
205         SetURI(SIP_DST_URI, -1);
206         SetAction(ID_MENU_CALL);
207         //SetCallStatus(call_info.state_text.ptr, call_info.state_text.slen);
208         SetCallStatus(call_info.last_status_text.ptr, call_info.last_status_text.slen);
209
210     } else {
211         //if (g_current_call == PJSUA_INVALID_ID)
212         //    g_current_call = call_id;
213
214         if (call_info.remote_contact.slen)
215             SetURI(call_info.remote_contact.ptr, call_info.remote_contact.slen, false);
216         else
217             SetURI(call_info.remote_info.ptr, call_info.remote_info.slen, false);
218
219         if (call_info.state == PJSIP_INV_STATE_CONFIRMED)
220             SetAction(ID_MENU_DISCONNECT);
221
222         SetCallStatus(call_info.state_text.ptr, call_info.state_text.slen);
223     }
224 }
225
226
227 /*
228  * Callback on media state changed event.
229  * The action may connect the call to sound device, to file, or
230  * to loop the call.
231  */
232 static void on_call_media_state(pjsua_call_id call_id)
233 {
234     pjsua_call_info call_info;
235
236     pjsua_call_get_info(call_id, &call_info);
237
238     if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
239         pjsua_conf_connect(call_info.conf_slot, 0);
240         pjsua_conf_connect(0, call_info.conf_slot);
241     }
242 }
243
244
245 /**
246  * Handler when there is incoming call.
247  */
248 static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
249                              pjsip_rx_data *rdata)
250 {
251     pjsua_call_info call_info;
252
253     PJ_UNUSED_ARG(acc_id);
254     PJ_UNUSED_ARG(rdata);
255
256     if (g_current_call != PJSUA_INVALID_ID) {
257         pj_str_t reason;
258         reason = pj_str("Another call is in progress");
259         pjsua_call_answer(call_id, PJSIP_SC_BUSY_HERE, &reason, NULL);
260         return;
261     }
262
263     g_current_call = call_id;
264
265     pjsua_call_get_info(call_id, &call_info);
266
267     SetAction(ID_MENU_ANSWER);
268     SetURI(call_info.remote_info.ptr, call_info.remote_info.slen, false);
269     pjsua_call_answer(call_id, 200, NULL, NULL);
270 }
271
272
273 /*
274  * Handler registration status has changed.
275  */
276 static void on_reg_state(pjsua_acc_id acc_id)
277 {
278     PJ_UNUSED_ARG(acc_id);
279
280     // Log already written.
281 }
282
283
284 /*
285  * Handler on buddy state changed.
286  */
287 static void on_buddy_state(pjsua_buddy_id buddy_id)
288 {
289     /* Currently this is not processed */
290     PJ_UNUSED_ARG(buddy_id);
291 }
292
293
294 /**
295  * Incoming IM message (i.e. MESSAGE request)!
296  */
297 static void on_pager(pjsua_call_id call_id, const pj_str_t *from, 
298                      const pj_str_t *to, const pj_str_t *contact,
299                      const pj_str_t *mime_type, const pj_str_t *text)
300 {
301     /* Currently this is not processed */
302     PJ_UNUSED_ARG(call_id);
303     PJ_UNUSED_ARG(from);
304     PJ_UNUSED_ARG(to);
305     PJ_UNUSED_ARG(contact);
306     PJ_UNUSED_ARG(mime_type);
307     PJ_UNUSED_ARG(text);
308 }
309
310
311 /**
312  * Received typing indication
313  */
314 static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
315                       const pj_str_t *to, const pj_str_t *contact,
316                       pj_bool_t is_typing)
317 {
318     /* Currently this is not processed */
319     PJ_UNUSED_ARG(call_id);
320     PJ_UNUSED_ARG(from);
321     PJ_UNUSED_ARG(to);
322     PJ_UNUSED_ARG(contact);
323     PJ_UNUSED_ARG(is_typing);
324 }
325
326 /** 
327  * Callback upon NAT detection completion 
328  */
329 static void nat_detect_cb(const pj_stun_nat_detect_result *res)
330 {
331     if (res->status != PJ_SUCCESS) {
332         char msg[250];
333         pj_ansi_snprintf(msg, sizeof(msg), "NAT detection failed: %s",
334                          res->status_text);
335         SetCallStatus(msg, pj_ansi_strlen(msg));
336     } else {
337         char msg[250];
338         pj_ansi_snprintf(msg, sizeof(msg), "NAT type is %s",
339                          res->nat_type_name);
340         SetCallStatus(msg, pj_ansi_strlen(msg));
341     }
342 }
343
344
345 static BOOL OnInitStack(void)
346 {
347     pjsua_config            cfg;
348     pjsua_logging_config    log_cfg;
349     pjsua_media_config      media_cfg;
350     pjsua_transport_config  udp_cfg;
351     pjsua_transport_config  rtp_cfg;
352     pjsua_transport_id      transport_id;
353     pjsua_transport_info    transport_info;
354     pj_str_t                tmp;
355     pj_status_t status;
356
357     /* Create pjsua */
358     status = pjsua_create();
359     if (status != PJ_SUCCESS) {
360         OnError(TEXT("Error creating pjsua"), status);
361         return FALSE;
362     }
363
364     /* Create global pool for application */
365     g_pool = pjsua_pool_create("pjsua", 4000, 4000);
366
367     /* Init configs */
368     pjsua_config_default(&cfg);
369     pjsua_media_config_default(&media_cfg);
370     pjsua_transport_config_default(&udp_cfg);
371     udp_cfg.port = SIP_PORT;
372
373     pjsua_transport_config_default(&rtp_cfg);
374     rtp_cfg.port = 40000;
375
376     pjsua_logging_config_default(&log_cfg);
377     log_cfg.level = 5;
378     log_cfg.log_filename = pj_str("\\pjsua.txt");
379     log_cfg.msg_logging = 1;
380     log_cfg.decor = pj_log_get_decor() | PJ_LOG_HAS_CR;
381
382     /* Setup media */
383     media_cfg.clock_rate = 8000;
384     media_cfg.ec_options = PJMEDIA_ECHO_SIMPLE;
385     media_cfg.ec_tail_len = 256;
386     // use default quality setting
387     //media_cfg.quality = 1;
388     media_cfg.ptime = 20;
389     media_cfg.enable_ice = USE_ICE;
390
391     /* Initialize application callbacks */
392     cfg.cb.on_call_state = &on_call_state;
393     cfg.cb.on_call_media_state = &on_call_media_state;
394     cfg.cb.on_incoming_call = &on_incoming_call;
395     cfg.cb.on_reg_state = &on_reg_state;
396     cfg.cb.on_buddy_state = &on_buddy_state;
397     cfg.cb.on_pager = &on_pager;
398     cfg.cb.on_typing = &on_typing;
399     cfg.cb.on_nat_detect = &nat_detect_cb;
400
401     if (SIP_PROXY) {
402             cfg.outbound_proxy_cnt = 1;
403             cfg.outbound_proxy[0] = pj_str(SIP_PROXY);
404     }
405     
406     if (NAMESERVER) {
407             cfg.nameserver_count = 1;
408             cfg.nameserver[0] = pj_str(NAMESERVER);
409     }
410     
411     if (NAMESERVER && STUN_DOMAIN) {
412             cfg.stun_domain = pj_str(STUN_DOMAIN);
413     } else if (STUN_SERVER) {
414             cfg.stun_host = pj_str(STUN_SERVER);
415     }
416     
417     
418     /* Initialize pjsua */
419     status = pjsua_init(&cfg, &log_cfg, &media_cfg);
420     if (status != PJ_SUCCESS) {
421         OnError(TEXT("Initialization error"), status);
422         return FALSE;
423     }
424
425     /* Set codec priority */
426     pjsua_codec_set_priority(pj_cstr(&tmp, "pcmu"), 240);
427     pjsua_codec_set_priority(pj_cstr(&tmp, "pcma"), 230);
428     pjsua_codec_set_priority(pj_cstr(&tmp, "speex/8000"), 190);
429     pjsua_codec_set_priority(pj_cstr(&tmp, "ilbc"), 189);
430     pjsua_codec_set_priority(pj_cstr(&tmp, "speex/16000"), 180);
431     pjsua_codec_set_priority(pj_cstr(&tmp, "speex/32000"), 0);
432     pjsua_codec_set_priority(pj_cstr(&tmp, "gsm"), 100);
433
434
435     /* Add UDP transport and the corresponding PJSUA account */
436     status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
437                                     &udp_cfg, &transport_id);
438     if (status != PJ_SUCCESS) {
439         OnError(TEXT("Error starting SIP transport"), status);
440         return FALSE;
441     }
442
443     pjsua_transport_get_info(transport_id, &transport_info);
444
445     g_local_uri.ptr = (char*)pj_pool_alloc(g_pool, 128);
446     g_local_uri.slen = pj_ansi_sprintf(g_local_uri.ptr,
447                                        "<sip:%.*s:%d>",
448                                        (int)transport_info.local_name.host.slen,
449                                        transport_info.local_name.host.ptr,
450                                        transport_info.local_name.port);
451
452
453     /* Add local account */
454     pjsua_acc_add_local(transport_id, PJ_TRUE, &g_current_acc);
455     pjsua_acc_set_online_status(g_current_acc, PJ_TRUE);
456
457     /* Add account */
458     if (HAS_SIP_ACCOUNT) {
459         pjsua_acc_config cfg;
460
461         pjsua_acc_config_default(&cfg);
462         cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN);
463         cfg.reg_uri = pj_str("sip:" SIP_DOMAIN);
464         cfg.cred_count = 1;
465         cfg.cred_info[0].realm = pj_str(SIP_REALM);
466         cfg.cred_info[0].scheme = pj_str("digest");
467         cfg.cred_info[0].username = pj_str(SIP_USER);
468         cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
469         cfg.cred_info[0].data = pj_str(SIP_PASSWD);
470
471         status = pjsua_acc_add(&cfg, PJ_TRUE, &g_current_acc);
472         if (status != PJ_SUCCESS) {
473             pjsua_destroy();
474             return PJ_FALSE;
475         }
476     }
477
478     /* Add buddy */
479     if (SIP_DST_URI) {
480         pjsua_buddy_config bcfg;
481     
482         pjsua_buddy_config_default(&bcfg);
483         bcfg.uri = pj_str(SIP_DST_URI);
484         bcfg.subscribe = PJ_FALSE;
485         
486         pjsua_buddy_add(&bcfg, NULL);
487     }
488
489     /* Start pjsua */
490     status = pjsua_start();
491     if (status != PJ_SUCCESS) {
492         OnError(TEXT("Error starting pjsua"), status);
493         return FALSE;
494     }
495
496     return TRUE;
497 }
498
499
500 //////////////////////////////////////////////////////////////////////////////
501
502 int WINAPI WinMain(HINSTANCE hInstance,
503                    HINSTANCE hPrevInstance,
504                    LPTSTR    lpCmdLine,
505                    int       nCmdShow)
506 {
507     MSG msg;
508     HACCEL hAccelTable;
509
510     PJ_UNUSED_ARG(lpCmdLine);
511     PJ_UNUSED_ARG(hPrevInstance);
512
513     // Perform application initialization:
514     if (!InitInstance (hInstance, nCmdShow)) 
515     {
516         return FALSE;
517     }
518     
519     hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_PJSUA_WINCE);
520     
521     
522     // Main message loop:
523     while (GetMessage(&msg, NULL, 0, 0)) 
524     {
525         if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
526         {
527             TranslateMessage(&msg);
528             DispatchMessage(&msg);
529         }
530     }
531     
532     return msg.wParam;
533 }
534
535 static ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
536 {
537     WNDCLASS wc;
538
539     wc.style            = CS_HREDRAW | CS_VREDRAW;
540     wc.lpfnWndProc      = (WNDPROC) WndProc;
541     wc.cbClsExtra       = 0;
542     wc.cbWndExtra       = 0;
543     wc.hInstance        = hInstance;
544     wc.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PJSUA_WINCE));
545     wc.hCursor          = 0;
546     wc.hbrBackground    = (HBRUSH) GetStockObject(WHITE_BRUSH);
547     wc.lpszMenuName     = 0;
548     wc.lpszClassName    = szWindowClass;
549
550     return RegisterClass(&wc);
551 }
552
553
554 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
555 {
556     HWND        hWnd;
557     TCHAR       szTitle[MAX_LOADSTRING];
558     TCHAR       szWindowClass[MAX_LOADSTRING];
559
560     hInst = hInstance;
561
562     /* Init stack */
563     if (OnInitStack() == FALSE)
564         return FALSE;
565
566     LoadString(hInstance, IDC_PJSUA_WINCE, szWindowClass, MAX_LOADSTRING);
567     MyRegisterClass(hInstance, szWindowClass);
568
569     LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
570     hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
571             CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 200, 
572             NULL, NULL, hInstance, NULL);
573
574     if (!hWnd)
575     {   
576             return FALSE;
577     }
578
579     hMainWnd = hWnd;
580     ShowWindow(hWnd, nCmdShow);
581     UpdateWindow(hWnd);
582     if (hwndCB)
583         CommandBar_Show(hwndCB, TRUE);
584
585     SetTimer(hMainWnd, ID_POLL_TIMER, 50, NULL);
586
587     pjsua_detect_nat_type();
588     return TRUE;
589 }
590
591
592 static void OnCreate(HWND hWnd)
593 {
594     enum 
595     {
596         X = 10,
597         Y = 40,
598         W = 220,
599         H = 30,
600     };
601
602     DWORD dwStyle;
603
604     hMainWnd = hWnd;
605
606     hwndCB = CommandBar_Create(hInst, hWnd, 1);                 
607     CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);   
608     CommandBar_AddAdornments(hwndCB, 0, 0);
609
610     // Create global status text
611     dwStyle = WS_CHILD | WS_VISIBLE | WS_DISABLED | ES_LEFT;  
612     hwndGlobalStatus = CreateWindow(
613                 TEXT("EDIT"),   // Class name
614                 NULL,           // Window text
615                 dwStyle,        // Window style
616                 X,              // x-coordinate of the upper-left corner
617                 Y+0,            // y-coordinate of the upper-left corner
618                 W,              // Width of the window for the edit
619                                 // control
620                 H-5,            // Height of the window for the edit
621                                 // control
622                 hWnd,           // Window handle to the parent window
623                 (HMENU) ID_GLOBAL_STATUS, // Control identifier
624                 hInst,           // Instance handle
625                 NULL);          // Specify NULL for this parameter when 
626                                 // you create a control
627     SetLocalURI(g_local_uri.ptr, g_local_uri.slen, false);
628
629
630     // Create URI edit
631     dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER;  
632     hwndURI = CreateWindow (
633                 TEXT("EDIT"),   // Class name
634                 NULL,           // Window text
635                 dwStyle,        // Window style
636                 X,  // x-coordinate of the upper-left corner
637                 Y+H,  // y-coordinate of the upper-left corner
638                 W,  // Width of the window for the edit
639                                 // control
640                 H-5,  // Height of the window for the edit
641                                 // control
642                 hWnd,           // Window handle to the parent window
643                 (HMENU) ID_URI, // Control identifier
644                 hInst,           // Instance handle
645                 NULL);          // Specify NULL for this parameter when 
646                                 // you create a control
647
648     // Create action Button
649     hwndActionButton = CreateWindow( L"button", L"Action", 
650                          WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 
651                          X, Y+2*H, 
652                          60, H-5, hWnd, 
653                          (HMENU) ID_BTN_ACTION,
654                          hInst, NULL );
655
656     // Create exit button
657     hwndExitButton = CreateWindow( L"button", L"E&xit", 
658                          WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 
659                          X+70, Y+2*H, 
660                          60, H-5, hWnd, 
661                          (HMENU) ID_EXIT,
662                          hInst, NULL );
663
664
665     // Create call status edit
666     dwStyle = WS_CHILD | WS_VISIBLE | WS_DISABLED;  
667     hwndCallStatus = CreateWindow (
668                 TEXT("EDIT"),   // Class name
669                 NULL,           // Window text
670                 dwStyle,        // Window style
671                 X,  // x-coordinate of the upper-left corner
672                 Y+3*H,  // y-coordinate of the upper-left corner
673                 W,  // Width of the window for the edit
674                                 // control
675                 H-5,  // Height of the window for the edit
676                                 // control
677                 hWnd,           // Window handle to the parent window
678                 (HMENU) ID_CALL_STATUS, // Control identifier
679                 hInst,           // Instance handle
680                 NULL);          // Specify NULL for this parameter when 
681                                 // you create a control
682     SetCallStatus("Ready", 5);
683     SetAction(ID_MENU_CALL);
684     SetURI(SIP_DST_URI, -1);
685     SetFocus(hWnd);
686
687 }
688
689
690 static void OnDestroy(void)
691 {
692     pjsua_destroy();
693 }
694
695 static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
696 {
697     int wmId, wmEvent;
698     
699     switch (message) {
700     case WM_KEYUP:
701         if (wParam==114) {
702             wParam = ID_MENU_CALL;
703         } else if (wParam==115) {
704             if (g_current_call == PJSUA_INVALID_ID)
705                 wParam = ID_EXIT;
706             else
707                 wParam = ID_MENU_DISCONNECT;
708         } else
709             break;
710
711     case WM_COMMAND:
712         wmId    = LOWORD(wParam); 
713         wmEvent = HIWORD(wParam); 
714         if (wmId == ID_BTN_ACTION)
715             wmId = g_current_action;
716         switch (wmId)
717         {
718         case ID_MENU_CALL:
719             if (g_current_call != PJSUA_INVALID_ID) {
720                 MessageBox(NULL, TEXT("Can not make call"), 
721                            TEXT("You already have one call active"), MB_OK);
722             }
723             pj_str_t dst_uri;
724             wchar_t text[256];
725             char tmp[256];
726             pj_status_t status;
727
728             GetWindowText(hwndURI, text, PJ_ARRAY_SIZE(text));
729             pj_unicode_to_ansi(text, pj_unicode_strlen(text),
730                                tmp, sizeof(tmp));
731             dst_uri.ptr = tmp;
732             dst_uri.slen = pj_ansi_strlen(tmp);
733             status = pjsua_call_make_call(g_current_acc,
734                                                       &dst_uri, 0, NULL,
735                                                       NULL, &g_current_call);
736             if (status != PJ_SUCCESS)
737                 OnError(TEXT("Unable to make call"), status);
738             break;
739         case ID_MENU_ANSWER:
740             if (g_current_call == PJSUA_INVALID_ID)
741                 MessageBox(NULL, TEXT("Can not answer"), 
742                            TEXT("There is no call!"), MB_OK);
743             else
744                 pjsua_call_answer(g_current_call, 200, NULL, NULL);
745             break;
746         case ID_MENU_DISCONNECT:
747             if (g_current_call == PJSUA_INVALID_ID)
748                 MessageBox(NULL, TEXT("Can not disconnect"), 
749                            TEXT("There is no call!"), MB_OK);
750             else
751                 pjsua_call_hangup(g_current_call, PJSIP_SC_DECLINE, NULL, NULL);
752             break;
753         case ID_EXIT:
754             DestroyWindow(hWnd);
755             break;
756         default:
757             return DefWindowProc(hWnd, message, wParam, lParam);
758         }
759         break;
760
761     case WM_CREATE:
762         OnCreate(hWnd);
763         break;
764
765     case WM_DESTROY:
766         OnDestroy();
767         CommandBar_Destroy(hwndCB);
768         PostQuitMessage(0);
769         break;
770
771     case WM_TIMER:
772         pjsua_handle_events(1);
773         break;
774
775     default:
776         return DefWindowProc(hWnd, message, wParam, lParam);
777     }
778    return 0;
779 }
780