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