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