1/*
2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3 *
4 *Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 *"Software"), to deal in the Software without restriction, including
7 *without limitation the rights to use, copy, modify, merge, publish,
8 *distribute, sublicense, and/or sell copies of the Software, and to
9 *permit persons to whom the Software is furnished to do so, subject to
10 *the following conditions:
11 *
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
14 *
15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 *Except as contained in this notice, the name of the XFree86 Project
24 *shall not be used in advertising or otherwise to promote the sale, use
25 *or other dealings in this Software without prior written authorization
26 *from the XFree86 Project.
27 *
28 * Authors: Alexander Gottwald
29 */
30
31#ifdef HAVE_XWIN_CONFIG_H
32#include <xwin-config.h>
33#endif
34#include "win.h"
35#include "winconfig.h"
36#include "winmsg.h"
37#include "globals.h"
38
39#include "xkbsrv.h"
40
41#ifdef XWIN_XF86CONFIG
42#ifndef CONFIGPATH
43#define CONFIGPATH  "%A," "%R," \
44                    "/etc/X11/%R," "%P/etc/X11/%R," \
45                    "%E," "%F," \
46                    "/etc/X11/%F," "%P/etc/X11/%F," \
47                    "/etc/X11/%X-%M," "/etc/X11/%X," "/etc/%X," \
48                    "%P/etc/X11/%X.%H," "%P/etc/X11/%X-%M," \
49                    "%P/etc/X11/%X," \
50                    "%P/lib/X11/%X.%H," "%P/lib/X11/%X-%M," \
51                    "%P/lib/X11/%X"
52#endif
53#ifndef CONFIGDIRPATH
54#define CONFIGDIRPATH  "/etc/X11/%X-%M," "/etc/X11/%X," "/etc/%X," \
55                       "%P/etc/X11/%X.%H," "%P/etc/X11/%X-%M," \
56                       "%P/etc/X11/%X," \
57                       "%P/lib/X11/%X.%H," "%P/lib/X11/%X-%M," \
58                       "%P/lib/X11/%X"
59#endif
60
61XF86ConfigPtr g_xf86configptr = NULL;
62#endif
63
64WinCmdlineRec g_cmdline = {
65#ifdef XWIN_XF86CONFIG
66    NULL,                       /* configFile */
67    NULL,                       /* configDir */
68#endif
69    NULL,                       /* fontPath */
70#ifdef XWIN_XF86CONFIG
71    NULL,                       /* keyboard */
72#endif
73    NULL,                       /* xkbRules */
74    NULL,                       /* xkbModel */
75    NULL,                       /* xkbLayout */
76    NULL,                       /* xkbVariant */
77    NULL,                       /* xkbOptions */
78    NULL,                       /* screenname */
79    NULL,                       /* mousename */
80    FALSE,                      /* emulate3Buttons */
81    0                           /* emulate3Timeout */
82};
83
84winInfoRec g_winInfo = {
85    {                           /* keyboard */
86     0,                         /* leds */
87     500,                       /* delay */
88     30                         /* rate */
89     }
90    ,
91    {                           /* xkb */
92     NULL,                      /* rules */
93     NULL,                      /* model */
94     NULL,                      /* layout */
95     NULL,                      /* variant */
96     NULL,                      /* options */
97     }
98    ,
99    {
100     FALSE,
101     50}
102};
103
104#define NULL_IF_EMPTY(x) (winNameCompare(x,"")?x:NULL)
105
106#ifdef XWIN_XF86CONFIG
107serverLayoutRec g_winConfigLayout;
108
109static Bool ParseOptionValue(int scrnIndex, void *options, OptionInfoPtr p);
110static Bool configLayout(serverLayoutPtr, XF86ConfLayoutPtr, char *);
111static Bool configImpliedLayout(serverLayoutPtr, XF86ConfScreenPtr);
112static Bool GetBoolValue(OptionInfoPtr p, const char *s);
113
114Bool
115winReadConfigfile()
116{
117    Bool retval = TRUE;
118    char *filename, *dirname;
119    MessageType filefrom = X_DEFAULT;
120    MessageType dirfrom = X_DEFAULT;
121    char *xf86ConfigFile = NULL;
122    char *xf86ConfigDir = NULL;
123
124    if (g_cmdline.configFile) {
125        filefrom = X_CMDLINE;
126        xf86ConfigFile = g_cmdline.configFile;
127    }
128    if (g_cmdline.configDir) {
129        dirfrom = X_CMDLINE;
130        xf86ConfigDir = g_cmdline.configDir;
131    }
132
133    /* Parse config file into data structure */
134    xf86initConfigFiles();
135    dirname = xf86openConfigDirFiles(CONFIGDIRPATH, xf86ConfigDir, PROJECTROOT);
136    filename = xf86openConfigFile(CONFIGPATH, xf86ConfigFile, PROJECTROOT);
137
138    /* Hack for backward compatibility */
139    if (!filename && from == X_DEFAULT)
140        filename = xf86openConfigFile(CONFIGPATH, "XF86Config", PROJECTROOT);
141
142    if (filename) {
143        winMsg(from, "Using config file: \"%s\"\n", filename);
144    }
145    else {
146        winMsg(X_ERROR, "Unable to locate/open config file");
147        if (xf86ConfigFile)
148            ErrorF(": \"%s\"", xf86ConfigFile);
149        ErrorF("\n");
150    }
151    if (dirname) {
152        winMsg(from, "Using config directory: \"%s\"\n", dirname);
153    }
154    else {
155        winMsg(X_ERROR, "Unable to locate/open config directory");
156        if (xf86ConfigDir)
157            ErrorF(": \"%s\"", xf86ConfigDir);
158        ErrorF("\n");
159    }
160    if (!filename && !dirname) {
161        return FALSE;
162    }
163    free(filename);
164    free(dirname);
165    if ((g_xf86configptr = xf86readConfigFile()) == NULL) {
166        winMsg(X_ERROR, "Problem parsing the config file\n");
167        return FALSE;
168    }
169    xf86closeConfigFile();
170
171    LogPrintMarkers();
172
173    /* set options from data structure */
174
175    if (g_xf86configptr->conf_layout_lst == NULL ||
176        g_cmdline.screenname != NULL) {
177        if (g_cmdline.screenname == NULL) {
178            winMsg(X_WARNING,
179                   "No Layout section. Using the first Screen section.\n");
180        }
181        if (!configImpliedLayout(&g_winConfigLayout,
182                                 g_xf86configptr->conf_screen_lst)) {
183            winMsg(X_ERROR, "Unable to determine the screen layout\n");
184            return FALSE;
185        }
186    }
187    else {
188        /* Check if layout is given in the config file */
189        if (g_xf86configptr->conf_flags != NULL) {
190            char *dfltlayout = NULL;
191            void *optlist = g_xf86configptr->conf_flags->flg_option_lst;
192
193            if (optlist && winFindOption(optlist, "defaultserverlayout"))
194                dfltlayout =
195                    winSetStrOption(optlist, "defaultserverlayout", NULL);
196
197            if (!configLayout(&g_winConfigLayout,
198                              g_xf86configptr->conf_layout_lst, dfltlayout)) {
199                winMsg(X_ERROR, "Unable to determine the screen layout\n");
200                return FALSE;
201            }
202        }
203        else {
204            if (!configLayout(&g_winConfigLayout,
205                              g_xf86configptr->conf_layout_lst, NULL)) {
206                winMsg(X_ERROR, "Unable to determine the screen layout\n");
207                return FALSE;
208            }
209        }
210    }
211
212    /* setup special config files */
213    winConfigFiles();
214    return retval;
215}
216#endif
217
218/* load layout definitions */
219#include "winlayouts.h"
220
221/* Set the keyboard configuration */
222Bool
223winConfigKeyboard(DeviceIntPtr pDevice)
224{
225    char layoutName[KL_NAMELENGTH];
226    unsigned char layoutFriendlyName[256];
227    unsigned int layoutNum = 0;
228    unsigned int deviceIdentifier = 0;
229    int keyboardType;
230
231#ifdef XWIN_XF86CONFIG
232    XF86ConfInputPtr kbd = NULL;
233    XF86ConfInputPtr input_list = NULL;
234    MessageType kbdfrom = X_CONFIG;
235#endif
236    MessageType from = X_DEFAULT;
237    char *s = NULL;
238
239    /* Setup defaults */
240    XkbGetRulesDflts(&g_winInfo.xkb);
241
242    /*
243     * Query the windows autorepeat settings and change the xserver defaults.
244     */
245    {
246        int kbd_delay;
247        DWORD kbd_speed;
248
249        if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &kbd_delay, 0) &&
250            SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &kbd_speed, 0)) {
251            switch (kbd_delay) {
252            case 0:
253                g_winInfo.keyboard.delay = 250;
254                break;
255            case 1:
256                g_winInfo.keyboard.delay = 500;
257                break;
258            case 2:
259                g_winInfo.keyboard.delay = 750;
260                break;
261            default:
262            case 3:
263                g_winInfo.keyboard.delay = 1000;
264                break;
265            }
266            g_winInfo.keyboard.rate = (kbd_speed > 0) ? kbd_speed : 1;
267            winMsg(X_PROBED, "Setting autorepeat to delay=%ld, rate=%ld\n",
268                   g_winInfo.keyboard.delay, g_winInfo.keyboard.rate);
269
270        }
271    }
272
273    keyboardType = GetKeyboardType(0);
274    if (keyboardType > 0 && GetKeyboardLayoutName(layoutName)) {
275        WinKBLayoutPtr pLayout;
276        Bool bfound = FALSE;
277        int pass;
278
279        layoutNum = strtoul(layoutName, (char **) NULL, 16);
280        if ((layoutNum & 0xffff) == 0x411) {
281            if (keyboardType == 7) {
282                /* Japanese layouts have problems with key event messages
283                   such as the lack of WM_KEYUP for Caps Lock key.
284                   Loading US layout fixes this problem. */
285                if (LoadKeyboardLayout("00000409", KLF_ACTIVATE) != NULL)
286                    winMsg(X_INFO, "Loading US keyboard layout.\n");
287                else
288                    winMsg(X_ERROR, "LoadKeyboardLayout failed.\n");
289            }
290        }
291
292        /* Discover the friendly name of the current layout */
293        {
294            HKEY regkey = NULL;
295            const char regtempl[] =
296                "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
297            char *regpath;
298            DWORD namesize = sizeof(layoutFriendlyName);
299
300            regpath = malloc(sizeof(regtempl) + KL_NAMELENGTH + 1);
301            strcpy(regpath, regtempl);
302            strcat(regpath, layoutName);
303
304            if (!RegOpenKey(HKEY_LOCAL_MACHINE, regpath, &regkey))
305                RegQueryValueEx(regkey, "Layout Text", 0, NULL,
306                                layoutFriendlyName, &namesize);
307
308            /* Close registry key */
309            if (regkey)
310                RegCloseKey(regkey);
311            free(regpath);
312        }
313
314        winMsg(X_PROBED,
315               "Windows keyboard layout: \"%s\" (%08x) \"%s\", type %d\n",
316               layoutName, layoutNum, layoutFriendlyName, keyboardType);
317
318        deviceIdentifier = layoutNum >> 16;
319        for (pass = 0; pass < 2; pass++) {
320            /* If we didn't find an exact match for the input locale identifier,
321               try to find an match on the language identifier part only  */
322            if (pass == 1)
323                layoutNum = (layoutNum & 0xffff);
324
325            for (pLayout = winKBLayouts; pLayout->winlayout != -1; pLayout++) {
326                if (pLayout->winlayout != layoutNum)
327                    continue;
328                if (pLayout->winkbtype > 0 && pLayout->winkbtype != keyboardType)
329                    continue;
330
331                bfound = TRUE;
332                winMsg(X_PROBED,
333                       "Found matching XKB configuration \"%s\"\n",
334                       pLayout->layoutname);
335
336                winMsg(X_PROBED,
337                       "Model = \"%s\" Layout = \"%s\""
338                       " Variant = \"%s\" Options = \"%s\"\n",
339                       pLayout->xkbmodel ? pLayout->xkbmodel : "none",
340                       pLayout->xkblayout ? pLayout->xkblayout : "none",
341                       pLayout->xkbvariant ? pLayout->xkbvariant : "none",
342                       pLayout->xkboptions ? pLayout->xkboptions : "none");
343
344                g_winInfo.xkb.model = pLayout->xkbmodel;
345                g_winInfo.xkb.layout = pLayout->xkblayout;
346                g_winInfo.xkb.variant = pLayout->xkbvariant;
347                g_winInfo.xkb.options = pLayout->xkboptions;
348
349                if (deviceIdentifier == 0xa000) {
350                    winMsg(X_PROBED, "Windows keyboard layout device identifier indicates Macintosh, setting Model = \"macintosh\"");
351                    g_winInfo.xkb.model = "macintosh";
352                }
353
354                break;
355            }
356
357            if (bfound)
358                break;
359        }
360
361        if (!bfound) {
362            winMsg(X_ERROR,
363                   "Keyboardlayout \"%s\" (%s) is unknown, using X server default layout\n",
364                   layoutFriendlyName, layoutName);
365        }
366    }
367
368    /* parse the configuration */
369#ifdef XWIN_XF86CONFIG
370    if (g_cmdline.keyboard)
371        kbdfrom = X_CMDLINE;
372
373    /*
374     * Until the layout code is finished, I search for the keyboard
375     * device and configure the server with it.
376     */
377
378    if (g_xf86configptr != NULL)
379        input_list = g_xf86configptr->conf_input_lst;
380
381    while (input_list != NULL) {
382        if (winNameCompare(input_list->inp_driver, "keyboard") == 0) {
383            /* Check if device name matches requested name */
384            if (g_cmdline.keyboard && winNameCompare(input_list->inp_identifier,
385                                                     g_cmdline.keyboard))
386                continue;
387            kbd = input_list;
388        }
389        input_list = input_list->list.next;
390    }
391
392    if (kbd != NULL) {
393
394        if (kbd->inp_identifier)
395            winMsg(kbdfrom, "Using keyboard \"%s\" as primary keyboard\n",
396                   kbd->inp_identifier);
397
398        if ((s = winSetStrOption(kbd->inp_option_lst, "AutoRepeat", NULL))) {
399            if ((sscanf(s, "%ld %ld", &g_winInfo.keyboard.delay,
400                        &g_winInfo.keyboard.rate) != 2) ||
401                (g_winInfo.keyboard.delay < 1) ||
402                (g_winInfo.keyboard.rate == 0) ||
403                (1000 / g_winInfo.keyboard.rate) < 1) {
404                winErrorFVerb(2, "\"%s\" is not a valid AutoRepeat value", s);
405                free(s);
406                return FALSE;
407            }
408            free(s);
409            winMsg(X_CONFIG, "AutoRepeat: %ld %ld\n",
410                   g_winInfo.keyboard.delay, g_winInfo.keyboard.rate);
411        }
412#endif
413
414        s = NULL;
415        if (g_cmdline.xkbRules) {
416            s = g_cmdline.xkbRules;
417            from = X_CMDLINE;
418        }
419#ifdef XWIN_XF86CONFIG
420        else {
421            s = winSetStrOption(kbd->inp_option_lst, "XkbRules", NULL);
422            from = X_CONFIG;
423        }
424#endif
425        if (s) {
426            g_winInfo.xkb.rules = NULL_IF_EMPTY(s);
427            winMsg(from, "XKB: rules: \"%s\"\n", s);
428        }
429
430        s = NULL;
431        if (g_cmdline.xkbModel) {
432            s = g_cmdline.xkbModel;
433            from = X_CMDLINE;
434        }
435#ifdef XWIN_XF86CONFIG
436        else {
437            s = winSetStrOption(kbd->inp_option_lst, "XkbModel", NULL);
438            from = X_CONFIG;
439        }
440#endif
441        if (s) {
442            g_winInfo.xkb.model = NULL_IF_EMPTY(s);
443            winMsg(from, "XKB: model: \"%s\"\n", s);
444        }
445
446        s = NULL;
447        if (g_cmdline.xkbLayout) {
448            s = g_cmdline.xkbLayout;
449            from = X_CMDLINE;
450        }
451#ifdef XWIN_XF86CONFIG
452        else {
453            s = winSetStrOption(kbd->inp_option_lst, "XkbLayout", NULL);
454            from = X_CONFIG;
455        }
456#endif
457        if (s) {
458            g_winInfo.xkb.layout = NULL_IF_EMPTY(s);
459            winMsg(from, "XKB: layout: \"%s\"\n", s);
460        }
461
462        s = NULL;
463        if (g_cmdline.xkbVariant) {
464            s = g_cmdline.xkbVariant;
465            from = X_CMDLINE;
466        }
467#ifdef XWIN_XF86CONFIG
468        else {
469            s = winSetStrOption(kbd->inp_option_lst, "XkbVariant", NULL);
470            from = X_CONFIG;
471        }
472#endif
473        if (s) {
474            g_winInfo.xkb.variant = NULL_IF_EMPTY(s);
475            winMsg(from, "XKB: variant: \"%s\"\n", s);
476        }
477
478        s = NULL;
479        if (g_cmdline.xkbOptions) {
480            s = g_cmdline.xkbOptions;
481            from = X_CMDLINE;
482        }
483#ifdef XWIN_XF86CONFIG
484        else {
485            s = winSetStrOption(kbd->inp_option_lst, "XkbOptions", NULL);
486            from = X_CONFIG;
487        }
488#endif
489        if (s) {
490            g_winInfo.xkb.options = NULL_IF_EMPTY(s);
491            winMsg(from, "XKB: options: \"%s\"\n", s);
492        }
493
494#ifdef XWIN_XF86CONFIG
495    }
496#endif
497
498    return TRUE;
499}
500
501#ifdef XWIN_XF86CONFIG
502Bool
503winConfigMouse(DeviceIntPtr pDevice)
504{
505    MessageType mousefrom = X_CONFIG;
506
507    XF86ConfInputPtr mouse = NULL;
508    XF86ConfInputPtr input_list = NULL;
509
510    if (g_cmdline.mouse)
511        mousefrom = X_CMDLINE;
512
513    if (g_xf86configptr != NULL)
514        input_list = g_xf86configptr->conf_input_lst;
515
516    while (input_list != NULL) {
517        if (winNameCompare(input_list->inp_driver, "mouse") == 0) {
518            /* Check if device name matches requested name */
519            if (g_cmdline.mouse && winNameCompare(input_list->inp_identifier,
520                                                  g_cmdline.mouse))
521                continue;
522            mouse = input_list;
523        }
524        input_list = input_list->list.next;
525    }
526
527    if (mouse != NULL) {
528        if (mouse->inp_identifier)
529            winMsg(mousefrom, "Using pointer \"%s\" as primary pointer\n",
530                   mouse->inp_identifier);
531
532        g_winInfo.pointer.emulate3Buttons =
533            winSetBoolOption(mouse->inp_option_lst, "Emulate3Buttons", FALSE);
534        if (g_cmdline.emulate3buttons)
535            g_winInfo.pointer.emulate3Buttons = g_cmdline.emulate3buttons;
536
537        g_winInfo.pointer.emulate3Timeout =
538            winSetIntOption(mouse->inp_option_lst, "Emulate3Timeout", 50);
539        if (g_cmdline.emulate3timeout)
540            g_winInfo.pointer.emulate3Timeout = g_cmdline.emulate3timeout;
541    }
542    else {
543        winMsg(X_ERROR, "No primary pointer configured\n");
544        winMsg(X_DEFAULT, "Using compiletime defaults for pointer\n");
545    }
546
547    return TRUE;
548}
549
550Bool
551winConfigFiles()
552{
553    MessageType from;
554    XF86ConfFilesPtr filesptr = NULL;
555
556    /* set some shortcuts */
557    if (g_xf86configptr != NULL) {
558        filesptr = g_xf86configptr->conf_files;
559    }
560
561    /* Fontpath */
562    from = X_DEFAULT;
563
564    if (g_cmdline.fontPath) {
565        from = X_CMDLINE;
566        defaultFontPath = g_cmdline.fontPath;
567    }
568    else if (filesptr != NULL && filesptr->file_fontpath) {
569        from = X_CONFIG;
570        defaultFontPath = strdup(filesptr->file_fontpath);
571    }
572    winMsg(from, "FontPath set to \"%s\"\n", defaultFontPath);
573
574    return TRUE;
575}
576#else
577Bool
578winConfigFiles(void)
579{
580    /* Fontpath */
581    if (g_cmdline.fontPath) {
582        defaultFontPath = g_cmdline.fontPath;
583        winMsg(X_CMDLINE, "FontPath set to \"%s\"\n", defaultFontPath);
584    }
585
586    return TRUE;
587}
588#endif
589
590Bool
591winConfigOptions(void)
592{
593    return TRUE;
594}
595
596Bool
597winConfigScreens(void)
598{
599    return TRUE;
600}
601
602#ifdef XWIN_XF86CONFIG
603char *
604winSetStrOption(void *optlist, const char *name, char *deflt)
605{
606    OptionInfoRec o;
607
608    o.name = name;
609    o.type = OPTV_STRING;
610    if (ParseOptionValue(-1, optlist, &o))
611        deflt = o.value.str;
612    if (deflt)
613        return strdup(deflt);
614    else
615        return NULL;
616}
617
618int
619winSetBoolOption(void *optlist, const char *name, int deflt)
620{
621    OptionInfoRec o;
622
623    o.name = name;
624    o.type = OPTV_BOOLEAN;
625    if (ParseOptionValue(-1, optlist, &o))
626        deflt = o.value.boolean;
627    return deflt;
628}
629
630int
631winSetIntOption(void *optlist, const char *name, int deflt)
632{
633    OptionInfoRec o;
634
635    o.name = name;
636    o.type = OPTV_INTEGER;
637    if (ParseOptionValue(-1, optlist, &o))
638        deflt = o.value.num;
639    return deflt;
640}
641
642double
643winSetRealOption(void *optlist, const char *name, double deflt)
644{
645    OptionInfoRec o;
646
647    o.name = name;
648    o.type = OPTV_REAL;
649    if (ParseOptionValue(-1, optlist, &o))
650        deflt = o.value.realnum;
651    return deflt;
652}
653
654double
655winSetPercentOption(void *optlist, const char *name, double deflt)
656{
657    OptionInfoRec o;
658
659    o.name = name;
660    o.type = OPTV_PERCENT;
661    if (ParseOptionValue(-1, optlist, &o))
662        deflt = o.value.realnum;
663    return deflt;
664}
665#endif
666
667/*
668 * Compare two strings for equality. This is caseinsensitive  and
669 * The characters '_', ' ' (space) and '\t' (tab) are treated as
670 * not existing.
671 */
672
673int
674winNameCompare(const char *s1, const char *s2)
675{
676    char c1, c2;
677
678    if (!s1 || *s1 == 0) {
679        if (!s2 || *s2 == 0)
680            return 0;
681        else
682            return 1;
683    }
684
685    while (*s1 == '_' || *s1 == ' ' || *s1 == '\t')
686        s1++;
687    while (*s2 == '_' || *s2 == ' ' || *s2 == '\t')
688        s2++;
689
690    c1 = (isupper((int) *s1) ? tolower((int) *s1) : *s1);
691    c2 = (isupper((int) *s2) ? tolower((int) *s2) : *s2);
692
693    while (c1 == c2) {
694        if (c1 == 0)
695            return 0;
696        s1++;
697        s2++;
698
699        while (*s1 == '_' || *s1 == ' ' || *s1 == '\t')
700            s1++;
701        while (*s2 == '_' || *s2 == ' ' || *s2 == '\t')
702            s2++;
703
704        c1 = (isupper((int) *s1) ? tolower((int) *s1) : *s1);
705        c2 = (isupper((int) *s2) ? tolower((int) *s2) : *s2);
706    }
707    return c1 - c2;
708}
709
710#ifdef XWIN_XF86CONFIG
711/*
712 * Find the named option in the list.
713 * @return the pointer to the option record, or NULL if not found.
714 */
715
716XF86OptionPtr
717winFindOption(XF86OptionPtr list, const char *name)
718{
719    while (list) {
720        if (winNameCompare(list->opt_name, name) == 0)
721            return list;
722        list = list->list.next;
723    }
724    return NULL;
725}
726
727/*
728 * Find the Value of an named option.
729 * @return The option value or NULL if not found.
730 */
731
732char *
733winFindOptionValue(XF86OptionPtr list, const char *name)
734{
735    list = winFindOption(list, name);
736    if (list) {
737        if (list->opt_val)
738            return list->opt_val;
739        else
740            return "";
741    }
742    return NULL;
743}
744
745/*
746 * Parse the option.
747 */
748
749static Bool
750ParseOptionValue(int scrnIndex, void *options, OptionInfoPtr p)
751{
752    char *s, *end;
753
754    if ((s = winFindOptionValue(options, p->name)) != NULL) {
755        switch (p->type) {
756        case OPTV_INTEGER:
757            if (*s == '\0') {
758                winDrvMsg(scrnIndex, X_WARNING,
759                          "Option \"%s\" requires an integer value\n", p->name);
760                p->found = FALSE;
761            }
762            else {
763                p->value.num = strtoul(s, &end, 0);
764                if (*end == '\0') {
765                    p->found = TRUE;
766                }
767                else {
768                    winDrvMsg(scrnIndex, X_WARNING,
769                              "Option \"%s\" requires an integer value\n",
770                              p->name);
771                    p->found = FALSE;
772                }
773            }
774            break;
775        case OPTV_STRING:
776            if (*s == '\0') {
777                winDrvMsg(scrnIndex, X_WARNING,
778                          "Option \"%s\" requires a string value\n", p->name);
779                p->found = FALSE;
780            }
781            else {
782                p->value.str = s;
783                p->found = TRUE;
784            }
785            break;
786        case OPTV_ANYSTR:
787            p->value.str = s;
788            p->found = TRUE;
789            break;
790        case OPTV_REAL:
791            if (*s == '\0') {
792                winDrvMsg(scrnIndex, X_WARNING,
793                          "Option \"%s\" requires a floating point value\n",
794                          p->name);
795                p->found = FALSE;
796            }
797            else {
798                p->value.realnum = strtod(s, &end);
799                if (*end == '\0') {
800                    p->found = TRUE;
801                }
802                else {
803                    winDrvMsg(scrnIndex, X_WARNING,
804                              "Option \"%s\" requires a floating point value\n",
805                              p->name);
806                    p->found = FALSE;
807                }
808            }
809            break;
810        case OPTV_BOOLEAN:
811            if (GetBoolValue(p, s)) {
812                p->found = TRUE;
813            }
814            else {
815                winDrvMsg(scrnIndex, X_WARNING,
816                          "Option \"%s\" requires a boolean value\n", p->name);
817                p->found = FALSE;
818            }
819            break;
820        case OPTV_PERCENT:
821            if (*s == '\0') {
822                winDrvMsg(scrnIndex, X_WARNING,
823                          "Option \"%s\" requires a percent value\n", p->name);
824                p->found = FALSE;
825            }
826            else {
827                double percent = strtod(s, &end);
828
829                if (end != s && winNameCompare(end, "%")) {
830                    p->found = TRUE;
831                    p->value.realnum = percent;
832                }
833                else {
834                    winDrvMsg(scrnIndex, X_WARNING,
835                              "Option \"%s\" requires a frequency value\n",
836                              p->name);
837                    p->found = FALSE;
838                }
839            }
840        case OPTV_FREQ:
841            if (*s == '\0') {
842                winDrvMsg(scrnIndex, X_WARNING,
843                          "Option \"%s\" requires a frequency value\n",
844                          p->name);
845                p->found = FALSE;
846            }
847            else {
848                double freq = strtod(s, &end);
849                int units = 0;
850
851                if (end != s) {
852                    p->found = TRUE;
853                    if (!winNameCompare(end, "Hz"))
854                        units = 1;
855                    else if (!winNameCompare(end, "kHz") ||
856                             !winNameCompare(end, "k"))
857                        units = 1000;
858                    else if (!winNameCompare(end, "MHz") ||
859                             !winNameCompare(end, "M"))
860                        units = 1000000;
861                    else {
862                        winDrvMsg(scrnIndex, X_WARNING,
863                                  "Option \"%s\" requires a frequency value\n",
864                                  p->name);
865                        p->found = FALSE;
866                    }
867                    if (p->found)
868                        freq *= (double) units;
869                }
870                else {
871                    winDrvMsg(scrnIndex, X_WARNING,
872                              "Option \"%s\" requires a frequency value\n",
873                              p->name);
874                    p->found = FALSE;
875                }
876                if (p->found) {
877                    p->value.freq.freq = freq;
878                    p->value.freq.units = units;
879                }
880            }
881            break;
882        case OPTV_NONE:
883            /* Should never get here */
884            p->found = FALSE;
885            break;
886        }
887        if (p->found) {
888            winDrvMsgVerb(scrnIndex, X_CONFIG, 2, "Option \"%s\"", p->name);
889            if (!(p->type == OPTV_BOOLEAN && *s == 0)) {
890                winErrorFVerb(2, " \"%s\"", s);
891            }
892            winErrorFVerb(2, "\n");
893        }
894    }
895    else if (p->type == OPTV_BOOLEAN) {
896        /* Look for matches with options with or without a "No" prefix. */
897        char *n, *newn;
898        OptionInfoRec opt;
899
900        n = winNormalizeName(p->name);
901        if (!n) {
902            p->found = FALSE;
903            return FALSE;
904        }
905        if (strncmp(n, "no", 2) == 0) {
906            newn = n + 2;
907        }
908        else {
909            free(n);
910            n = malloc(strlen(p->name) + 2 + 1);
911            if (!n) {
912                p->found = FALSE;
913                return FALSE;
914            }
915            strcpy(n, "No");
916            strcat(n, p->name);
917            newn = n;
918        }
919        if ((s = winFindOptionValue(options, newn)) != NULL) {
920            if (GetBoolValue(&opt, s)) {
921                p->value.boolean = !opt.value.boolean;
922                p->found = TRUE;
923            }
924            else {
925                winDrvMsg(scrnIndex, X_WARNING,
926                          "Option \"%s\" requires a boolean value\n", newn);
927                p->found = FALSE;
928            }
929        }
930        else {
931            p->found = FALSE;
932        }
933        if (p->found) {
934            winDrvMsgVerb(scrnIndex, X_CONFIG, 2, "Option \"%s\"", newn);
935            if (*s != 0) {
936                winErrorFVerb(2, " \"%s\"", s);
937            }
938            winErrorFVerb(2, "\n");
939        }
940        free(n);
941    }
942    else {
943        p->found = FALSE;
944    }
945    return p->found;
946}
947
948static Bool
949configLayout(serverLayoutPtr servlayoutp, XF86ConfLayoutPtr conf_layout,
950             char *default_layout)
951{
952#if 0
953#pragma warn UNIMPLEMENTED
954#endif
955    return TRUE;
956}
957
958static Bool
959configImpliedLayout(serverLayoutPtr servlayoutp, XF86ConfScreenPtr conf_screen)
960{
961#if 0
962#pragma warn UNIMPLEMENTED
963#endif
964    return TRUE;
965}
966
967static Bool
968GetBoolValue(OptionInfoPtr p, const char *s)
969{
970    if (*s == 0) {
971        p->value.boolean = TRUE;
972    }
973    else {
974        if (winNameCompare(s, "1") == 0)
975            p->value.boolean = TRUE;
976        else if (winNameCompare(s, "on") == 0)
977            p->value.boolean = TRUE;
978        else if (winNameCompare(s, "true") == 0)
979            p->value.boolean = TRUE;
980        else if (winNameCompare(s, "yes") == 0)
981            p->value.boolean = TRUE;
982        else if (winNameCompare(s, "0") == 0)
983            p->value.boolean = FALSE;
984        else if (winNameCompare(s, "off") == 0)
985            p->value.boolean = FALSE;
986        else if (winNameCompare(s, "false") == 0)
987            p->value.boolean = FALSE;
988        else if (winNameCompare(s, "no") == 0)
989            p->value.boolean = FALSE;
990    }
991    return TRUE;
992}
993#endif
994
995char *
996winNormalizeName(const char *s)
997{
998    char *ret, *q;
999    const char *p;
1000
1001    if (s == NULL)
1002        return NULL;
1003
1004    ret = malloc(strlen(s) + 1);
1005    for (p = s, q = ret; *p != 0; p++) {
1006        switch (*p) {
1007        case '_':
1008        case ' ':
1009        case '\t':
1010            continue;
1011        default:
1012            if (isupper((int) *p))
1013                *q++ = tolower((int) *p);
1014            else
1015                *q++ = *p;
1016        }
1017    }
1018    *q = '\0';
1019    return ret;
1020}
1021