Fix messages sent when right-clicking in a blank (non-item) area of a
[wine] / dlls / serialui / confdlg.c
1 /*              
2  * This DLL contains the user interface for the serial driver.
3  *    a dialog box to configure the specified COMM port
4  *    an interface to the control panel (??)
5  *    functions to load and save default configuration
6  *
7  * Eventually the 32 bit comm port driver could be moved into here
8  * and interfaced to KERNEL32 using the WIN95 or WINNT comm driver interface.
9  * This way, different driver DLLS could be written to support other
10  * serial interfaces, such as X.25, etc.
11  *
12  * Basic structure copied from COMCTL32 code.
13  *
14  * Copyright 2000 Mike McCormack
15  */
16
17 #include <string.h>
18 #include <stdio.h>
19
20 #include "winbase.h"
21 #include "winreg.h"
22 #include "wingdi.h"
23 #include "winuser.h"
24 #include "debugtools.h"
25 #include "serialui.h"
26 #include "winerror.h"
27
28 DEFAULT_DEBUG_CHANNEL(comm);
29
30 HMODULE SERIALUI_hModule = 0;
31 DWORD SERIALUI_dwProcessesAttached = 0;
32
33 /***********************************************************************
34  * SERIALUI_LibMain [Internal] Initializes the internal 'SERIALUI.DLL'.
35  *
36  * PARAMS
37  *     hinstDLL    [I] handle to the DLL's instance
38  *     fdwReason   [I]
39  *     lpvReserved [I] reserved, must be NULL
40  *
41  * RETURNS
42  *     Success: TRUE
43  *     Failure: FALSE
44  */
45
46 BOOL WINAPI
47 SERIALUI_LibMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
48 {
49     TRACE("%x,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
50
51     switch (fdwReason) {
52         case DLL_PROCESS_ATTACH:
53             if (SERIALUI_dwProcessesAttached == 0) {
54
55                 /* This will be wrong for any other process attching in this address-space! */
56                 SERIALUI_hModule = (HMODULE)hinstDLL;
57
58             }
59             SERIALUI_dwProcessesAttached++;
60             break;
61
62         case DLL_PROCESS_DETACH:
63             SERIALUI_dwProcessesAttached--;
64             if (SERIALUI_dwProcessesAttached == 0) {
65                 TRACE("Last Process detached\n");
66             }
67             break;
68     }
69
70     return TRUE;
71 }
72
73
74 /***********************************************************************
75  * SERIALUI_EnumPropPages (SERIALUI.2)
76  *
77  * Called by the device manager to add prop sheets in Control Panel ???
78  * Pointed to in Win98 registry by 
79  * \System\CurrentControlSet\Services\Class\ports\0000\EnumPropPages =
80  *  "serialui.dll,EnumPropPages"
81  */
82 typedef LPVOID LPDEVICE_INFO;
83 typedef LPVOID LPFNADDPROPSHEETPAGE;
84 BOOL WINAPI SERIALUI_EnumPropPages(LPDEVICE_INFO pdi, LPFNADDPROPSHEETPAGE pfnAdd, LPARAM lParam )
85 {
86     FIXME("(%p %p %lx)\n",pdi,pfnAdd,lParam);
87     return FALSE;
88 }
89
90 /*
91  * These data structures are convert from values used in fields of a DCB
92  * to strings used in the CommConfigDialog.
93  */
94 typedef struct tagPARAM2STRDATA
95 {
96     DWORD        val;
97     CONST CHAR  *name;
98 } PARAM2STRDATA, *LPPARAM2STRDATA;
99
100 typedef struct tagPARAM2STR
101 {
102     DWORD         dwSize;
103     LPPARAM2STRDATA data;
104 } PARAM2STR, *LPPARAM2STR;
105 typedef const LPPARAM2STR LPCPARAM2STR;
106
107 #define SERIALUI_TABLESIZE(x) ((sizeof (x))/(sizeof (x[0])))
108
109 static PARAM2STRDATA SERIALUI_Baud2StrData[]={
110   {110, "110"}, {300, "300"}, {600, "600"}, {1200, "1200"},
111   {2400, "2400"}, {4800, "4800"}, {9600, "9600"}, {14400, "14400"},
112   {19200, "19200"}, {38400L, "38400"}, {56000L, "56000"}, {57600L, "57600"},
113   {115200L, "115200"}, {128000L, "128000"}, {256000L, "256000"}
114 };
115 static PARAM2STR SERIALUI_Baud2Str={ SERIALUI_TABLESIZE(SERIALUI_Baud2StrData),SERIALUI_Baud2StrData };
116
117 static PARAM2STRDATA SERIALUI_Parity2StrData[]={ 
118   {NOPARITY,"None"}, {ODDPARITY,"Odd"}, {EVENPARITY,"Even"}, {MARKPARITY,"Mark"},
119   {SPACEPARITY,"Space"}
120 };
121 static PARAM2STR SERIALUI_Parity2Str={ SERIALUI_TABLESIZE(SERIALUI_Parity2StrData),SERIALUI_Parity2StrData };
122
123 static PARAM2STRDATA SERIALUI_Stop2StrData[]={
124   {ONESTOPBIT,"1"}, {ONE5STOPBITS,"1.5"}, {TWOSTOPBITS,"2"}
125 };
126 static PARAM2STR SERIALUI_Stop2Str={ SERIALUI_TABLESIZE(SERIALUI_Stop2StrData),SERIALUI_Stop2StrData };
127
128 static PARAM2STRDATA SERIALUI_Data2StrData[]={
129   {5,"5"}, {6,"6"}, {7,"7"}, {8, "8"}, {16,"16"}
130 };
131 static PARAM2STR SERIALUI_Data2Str={ SERIALUI_TABLESIZE(SERIALUI_Data2StrData),SERIALUI_Data2StrData };
132
133 static PARAM2STRDATA SERIALUI_Flow2StrData[]={
134   {0,"None"}, {1,"Hardware (RTS/CTS)"}, {2,"Software (XON/XOFF)"}
135 };
136 static PARAM2STR SERIALUI_Flow2Str={ SERIALUI_TABLESIZE(SERIALUI_Flow2StrData),SERIALUI_Flow2StrData };
137
138 /*
139  * Add all the fields to a combo box and highlight the current value
140  */
141 static void SERIALUI_AddConfItems(HWND hDlg, DWORD id, LPCPARAM2STR table, DWORD dwVal)
142 {
143     int i,n;
144     HWND hControl = GetDlgItem(hDlg,id);
145
146     if(!hControl)
147         return;
148
149     for(i=0; i<table->dwSize; i++)
150     {
151         n = SendMessageA(hControl, CB_ADDSTRING, 0L, (LPARAM)table->data[i].name);
152         if(dwVal == table->data[i].val)
153         {
154             SendMessageA(hControl, CB_SETCURSEL, (WPARAM)n, (LPARAM)0);
155         }
156     }
157 }
158
159 /*
160  * Get the current sellection of the given combo box and set a DCB field to
161  * the value matching that selection.
162  */
163 static BOOL SERIALUI_GetConfItems(HWND hDlg, DWORD id, LPCPARAM2STR table, LPDWORD lpdwVal)
164 {
165     DWORD i;
166     CHAR lpEntry[20];
167     HWND hControl = GetDlgItem(hDlg,id);
168
169     if( (!hControl) || (!lpdwVal))
170     {
171         TRACE("Couldn't get window handle for item %lx\n",id);
172         return FALSE;
173     }
174
175     if(!GetWindowTextA(hControl, &lpEntry[0], sizeof lpEntry))
176     {
177         TRACE("Couldn't get window text for item %lx\n",id);
178         return FALSE;
179     }
180     /* TRACE("%ld contains %s\n",id, lpEntry); */
181
182     for(i=0; i<table->dwSize; i++)
183     {
184         if(!lstrcmpA(table->data[i].name,lpEntry))
185         {
186             *lpdwVal = table->data[i].val;
187             return TRUE;
188         }
189     }
190
191     return FALSE;
192 }
193
194 /*
195  * Both the enumerated values CBR_XXXX and integer baud rates are valid
196  * dcb.BaudRate. This code is to convert back and forth between CBR_ style
197  * and integers. The dialog box uses integer values.
198  */
199 static DWORD SERIALUI_BaudConvertTable[] =  {
200   CBR_110, 110, CBR_300, 300, CBR_600, 600, CBR_1200, 1200,
201   CBR_2400, 2400, CBR_4800, 4800, CBR_9600, 9600, CBR_14400, 14400,
202   CBR_19200, 19200, CBR_38400, 38400, CBR_56000, 56000, CBR_57600, 57600,
203   CBR_115200, 115200, CBR_128000, 128000, CBR_256000, 256000
204 };
205
206 static BOOL SERIALUI_MakeBaudDword(LPDWORD lpdwBaudRate)
207 {
208     int i;
209
210     for(i=0; i<(sizeof(SERIALUI_BaudConvertTable)/sizeof(DWORD)); i+=2)
211     {
212         if(*lpdwBaudRate == SERIALUI_BaudConvertTable[i])
213         {
214             *lpdwBaudRate = SERIALUI_BaudConvertTable[i+1];
215             return TRUE;
216         }
217     }
218     return FALSE;
219 }
220
221 static BOOL SERIALUI_MakeBaudEnum(LPDWORD lpdwBaudRate)
222 {
223     int i;
224
225     for(i=0; i<(sizeof(SERIALUI_BaudConvertTable)/sizeof(DWORD)); i+=2)
226     {
227         if(*lpdwBaudRate == SERIALUI_BaudConvertTable[i+1])
228         {
229             *lpdwBaudRate = SERIALUI_BaudConvertTable[i];
230             return TRUE;
231         }
232     }
233     return FALSE;
234 }
235
236 typedef struct tagSERIALUI_DialogInfo
237 {
238     LPCSTR lpszDevice;
239     LPCOMMCONFIG lpCommConfig;
240     BOOL bConvert; /* baud rate was converted to a DWORD */
241     DWORD dwFlowControl; /* old flow control */
242 } SERIALUI_DialogInfo;
243
244 static void SERIALUI_DCBToDialogInfo(HWND hDlg, SERIALUI_DialogInfo *info)
245 {
246     DWORD dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl;
247     LPDCB lpdcb = &info->lpCommConfig->dcb;
248
249     /* pass integer pointers to SERIALUI_ dialog config fns */
250     dwBaudRate    = lpdcb->BaudRate;
251     dwStopBits    = lpdcb->StopBits;
252     dwParity      = lpdcb->Parity;
253     dwByteSize    = lpdcb->ByteSize;
254
255     /* map flow control state, if it looks normal */
256     if((lpdcb->fRtsControl == RTS_CONTROL_HANDSHAKE) ||
257        (lpdcb->fOutxCtsFlow == TRUE)) {
258         dwFlowControl = 1;
259     } else if(lpdcb->fOutX || lpdcb->fInX) {
260         dwFlowControl = 2;
261     } else {
262         dwFlowControl = 0;
263     }
264
265     info->bConvert = SERIALUI_MakeBaudDword(&dwBaudRate);
266
267     SERIALUI_AddConfItems( hDlg, IDC_BAUD, &SERIALUI_Baud2Str ,dwBaudRate);
268     SERIALUI_AddConfItems( hDlg, IDC_STOP, &SERIALUI_Stop2Str ,dwStopBits);
269     SERIALUI_AddConfItems( hDlg, IDC_PARITY, &SERIALUI_Parity2Str ,dwParity);
270     SERIALUI_AddConfItems( hDlg, IDC_DATA, &SERIALUI_Data2Str ,dwByteSize);
271     SERIALUI_AddConfItems( hDlg, IDC_FLOW, &SERIALUI_Flow2Str, dwFlowControl );
272
273     info->dwFlowControl = dwFlowControl;
274 }
275
276 static void SERIALUI_DialogInfoToDCB(HWND hDlg, SERIALUI_DialogInfo *info)
277 {
278     DWORD dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl;
279     LPDCB lpdcb = &info->lpCommConfig->dcb;
280
281     SERIALUI_GetConfItems( hDlg, IDC_BAUD, &SERIALUI_Baud2Str, &dwBaudRate);
282     SERIALUI_GetConfItems( hDlg, IDC_STOP, &SERIALUI_Stop2Str, &dwStopBits);
283     SERIALUI_GetConfItems( hDlg, IDC_PARITY, &SERIALUI_Parity2Str, &dwParity);
284     SERIALUI_GetConfItems( hDlg, IDC_DATA, &SERIALUI_Data2Str, &dwByteSize);
285     SERIALUI_GetConfItems( hDlg, IDC_FLOW, &SERIALUI_Flow2Str, &dwFlowControl );
286  
287     TRACE("baud=%ld stop=%ld parity=%ld data=%ld flow=%ld\n",
288           dwBaudRate, dwStopBits, dwParity, dwByteSize, dwFlowControl);
289     
290     lpdcb->BaudRate = dwBaudRate;
291     lpdcb->StopBits = dwStopBits;
292     lpdcb->Parity   = dwParity;
293     lpdcb->ByteSize = dwByteSize;
294
295     /* try not to change flow control if the user didn't change it */
296     if(info->dwFlowControl != dwFlowControl)
297     {
298         switch(dwFlowControl)
299         {
300         case 0:
301             lpdcb->fOutxCtsFlow = FALSE;
302             lpdcb->fOutxDsrFlow = FALSE;
303             lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
304             lpdcb->fOutX        = FALSE;
305             lpdcb->fInX         = FALSE;
306             lpdcb->fRtsControl  = RTS_CONTROL_DISABLE;
307             break;
308         case 1: /* CTS/RTS */
309             lpdcb->fOutxCtsFlow = TRUE;
310             lpdcb->fOutxDsrFlow = FALSE;
311             lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
312             lpdcb->fOutX        = FALSE;
313             lpdcb->fInX         = FALSE;
314             lpdcb->fRtsControl  = RTS_CONTROL_HANDSHAKE;
315             break;
316         case 2:
317             lpdcb->fOutxCtsFlow = FALSE;
318             lpdcb->fOutxDsrFlow = FALSE;
319             lpdcb->fDtrControl  = DTR_CONTROL_DISABLE;
320             lpdcb->fOutX        = TRUE;
321             lpdcb->fInX         = TRUE;
322             lpdcb->fRtsControl  = RTS_CONTROL_DISABLE;
323             break;
324         }
325     }
326
327     if(info->bConvert)
328         SERIALUI_MakeBaudEnum(&lpdcb->BaudRate);
329 }
330
331 /***********************************************************************
332  * SERIALUI_ConfigDialogProc
333  *
334  * Shows a dialog for configuring a COMM port
335  */
336 BOOL WINAPI SERIALUI_ConfigDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
337 {
338     CHAR szTitle[30];
339     SERIALUI_DialogInfo *info;
340
341     switch (uMsg)
342     {
343     case WM_INITDIALOG:
344         info = (SERIALUI_DialogInfo*) lParam;
345         if(!info)
346             return FALSE;
347         SetWindowLongA(hWnd, DWL_USER, lParam);
348         snprintf(szTitle, sizeof szTitle, "Settings for %s", info->lpszDevice);
349         SetWindowTextA(hWnd, szTitle);
350         SERIALUI_DCBToDialogInfo(hWnd, info);
351         return TRUE;
352
353     case WM_COMMAND:
354     {
355         WORD wID = LOWORD(wParam);
356
357         info = (SERIALUI_DialogInfo *) GetWindowLongA(hWnd, DWL_USER);
358         if(!info)
359             EndDialog(hWnd,0);
360         switch (wID)
361         {
362         case IDOK:
363             SERIALUI_DialogInfoToDCB(hWnd,info);
364             EndDialog(hWnd,1);
365             return TRUE;
366         case IDCANCEL:
367             EndDialog(hWnd,0);
368             return TRUE;
369 /* test code for Get/SetDefaultCommConfig begins */
370         case ID_GETDEFAULT:
371             {
372                 DWORD r,dwConfSize = sizeof (COMMCONFIG);
373                 r = GetDefaultCommConfigA(info->lpszDevice, 
374                           info->lpCommConfig, &dwConfSize);
375                 if(!r)
376                     MessageBoxA(hWnd,"Failed","GetDefaultCommConfig",MB_OK);
377             }
378             SERIALUI_DCBToDialogInfo(hWnd, info);
379             break;
380         case ID_SETDEFAULT:
381             {
382                 DWORD r;
383                 SERIALUI_DialogInfoToDCB(hWnd,info);
384                 r = SetDefaultCommConfigA(info->lpszDevice, 
385                           info->lpCommConfig, sizeof (COMMCONFIG));
386                 if(!r)
387                     MessageBoxA(hWnd,"Failed","GetDefaultCommConfig",MB_OK);
388             }
389             break;
390 /* test code for Get/SetDefaultCommConfig ends */
391         }
392     }
393     default:
394         return FALSE;
395     }
396 }
397
398 /***********************************************************************
399  * SERIALUI_CommConfigDialog (SERIALUI.3)
400  *
401  * Used by Win9x KERNEL to show a dialog for configuring a COMM port.
402  */
403 BOOL WINAPI SERIALUI_CommConfigDialog(
404         LPCSTR lpszName, 
405         HWND hWndParent, 
406         LPCOMMCONFIG lpCommConfig
407 ) {
408     SERIALUI_DialogInfo info;
409
410     info.lpCommConfig  = lpCommConfig;
411     info.lpszDevice    = lpszName;
412     info.bConvert      = FALSE;
413     info.dwFlowControl = 0;
414
415     if(!lpCommConfig)
416         return FALSE;
417
418     return DialogBoxParamA(SERIALUI_hModule,
419                            MAKEINTRESOURCEA(IDD_SERIALUICONFIG),
420                            hWndParent, 
421                            (DLGPROC) SERIALUI_ConfigDialogProc,
422                            (LPARAM)&info);
423 }
424
425 static LPCSTR lpszCommKey = "System\\CurrentControlSet\\Services\\Class\\Ports";
426 static LPCSTR lpszDCB     = "DCB";
427
428 /***********************************************************************
429  * SERIALUI_SetDefaultCommConfig (SERIALUI.4)
430  *
431  * Used by Win98 KERNEL to set the default config for a COMM port
432  * FIXME: uses the wrong registry key... should use a digit, not
433  *        the comm port name.
434  */
435 BOOL WINAPI SERIALUI_SetDefaultCommConfig(
436         LPCSTR lpszDevice, 
437         LPCOMMCONFIG lpCommConfig,
438         DWORD dwSize
439 ) {
440     HKEY hKeyReg=0, hKeyPort=0;
441     CHAR szKeyName[100];
442     DWORD r,dwDCBSize;
443
444     TRACE("%p %p %lx\n",lpszDevice,lpCommConfig,dwSize);
445
446     if(!lpCommConfig)
447         return FALSE;
448
449     if(dwSize < sizeof (COMMCONFIG))
450         return FALSE;
451
452     r = RegConnectRegistryA(NULL, HKEY_LOCAL_MACHINE, &hKeyReg);
453     if(r != ERROR_SUCCESS)
454         return FALSE;
455
456     snprintf(szKeyName, sizeof szKeyName, "%s\\%s", lpszCommKey ,lpszDevice);
457     r = RegCreateKeyA(hKeyReg, szKeyName, &hKeyPort);
458     if(r == ERROR_SUCCESS)
459     {
460         dwDCBSize = sizeof (DCB);
461         r = RegSetValueExA( hKeyPort, lpszDCB, NULL, REG_BINARY, 
462                             (LPSTR)&lpCommConfig->dcb,dwDCBSize);
463         TRACE("write key r=%ld\n",r);
464         RegCloseKey(hKeyPort);
465     }
466
467     RegCloseKey(hKeyReg);
468
469     return (r==ERROR_SUCCESS);
470 }
471
472 /***********************************************************************
473  * SERIALUI_GetDefaultCommConfig (SERIALUI.5)
474  *
475  * Used by Win9x KERNEL to get the default config for a COMM port
476  * FIXME: uses the wrong registry key... should use a digit, not
477  *        the comm port name.
478  */
479 BOOL WINAPI SERIALUI_GetDefaultCommConfig(
480         LPCSTR lpszDevice, 
481         LPCOMMCONFIG lpCommConfig,
482         LPDWORD lpdwSize
483 ) {
484     HKEY hKeyReg, hKeyPort;
485     CHAR szKeyName[100];
486     DWORD r,dwSize,dwType;
487
488     TRACE("%p %p %p\n",lpszDevice,lpCommConfig,lpdwSize);
489
490     if(!lpCommConfig)
491         return FALSE;
492
493     if(!lpdwSize)
494         return FALSE;
495
496     if(*lpdwSize < sizeof (COMMCONFIG))
497         return FALSE;
498
499     *lpdwSize = sizeof (COMMCONFIG);
500     memset(lpCommConfig, 0 , sizeof (COMMCONFIG));
501     lpCommConfig->dwSize = sizeof (COMMCONFIG);
502     lpCommConfig->wVersion = 1;
503
504     r = RegConnectRegistryA(NULL, HKEY_LOCAL_MACHINE, &hKeyReg);
505     if(r != ERROR_SUCCESS)
506         return FALSE;
507
508     snprintf(szKeyName, sizeof szKeyName, "%s\\%s", lpszCommKey ,lpszDevice);
509     r = RegOpenKeyA(hKeyReg, szKeyName, &hKeyPort);
510     if(r == ERROR_SUCCESS)
511     {
512         dwSize = sizeof (DCB);
513         dwType = 0;
514         r = RegQueryValueExA( hKeyPort, lpszDCB, NULL,
515                              &dwType, (LPSTR)&lpCommConfig->dcb,&dwSize);
516         if ((r==ERROR_SUCCESS) && (dwType != REG_BINARY))
517             r = 1;
518         if ((r==ERROR_SUCCESS) && (dwSize != sizeof(DCB)))
519             r = 1;
520            
521         RegCloseKey(hKeyPort);
522     }
523     else
524     {
525         /* FIXME: default to a hardcoded commconfig */
526         
527         lpCommConfig->dcb.DCBlength = sizeof(DCB);
528         lpCommConfig->dcb.BaudRate = 9600;
529         lpCommConfig->dcb.fBinary = TRUE;
530         lpCommConfig->dcb.fParity = FALSE;
531         lpCommConfig->dcb.ByteSize = 8;
532         lpCommConfig->dcb.Parity = NOPARITY;
533         lpCommConfig->dcb.StopBits = ONESTOPBIT;
534         return TRUE;
535     }
536
537     RegCloseKey(hKeyReg);
538
539     return (r==ERROR_SUCCESS);
540 }