Hands-on Projects for the Linux Graphics Subsystem
|
The xorg configuration file for the X Server is called xorg.conf. It is described by the xorg.conf man page. The configuration starts with xf86HandleConfigFile(), which is called as:
xf86HandleConfigFile(FALSE) |
xf86HandleConfigFile() calls xf86openConfigFile() to open xorg.conf (or however else is called):
filename = xf86openConfigFile(searchpath, xf86ConfigFile, PROJECTROOT); |
As the comment in the xf86openConfigFile() source file indicates:
* This function take a config file search path (optional), a command-line * specified file name (optional) and the ProjectRoot path (optional) and * locates and opens a config file based on that information. If a * command-line file name is specified, then this function fails if none * of the located files. * * The return value is a pointer to the actual name of the file that was * opened. When no file is found, the return value is NULL. * |
As seen in the xorg.conf man page the paths where the configuration file, usually xorg.conf is found is described by DEFAULT_CONF_PATH:
#define DEFAULT_CONF_PATH "/etc/X11/%S," \ "%P/etc/X11/%S," \ "/etc/X11/%G," \ "%P/etc/X11/%G," \ "/etc/X11/%X-%M," \ "/etc/X11/%X," \ "/etc/%X," \ "%P/etc/X11/%X.%H," \ "%P/etc/X11/%X-%M," \ "%P/etc/X11/%X," \ "%P/lib/X11/%X.%H," \ "%P/lib/X11/%X-%M," \ "%P/lib/X11/%X" |
strtok() is used to obtain the first path from DEFAULT_CONF_PATH. The paths are separated by commas (',').
template = strtok(pathcopy, ","); |
DoSubstitution() is called to form the possible paths given by combining one of the paths of DEFAULT_CONF_PATH and XCONFIGFILE, which is defined as xorg.conf, also in scan.c. The escape sequences, e.g. %A, %X, etc described in scan.c are also replaced.
Also the configFile handle is returned from the fopen() call that opens the configuration file.
if ((configFile = fopen(configPath, "r")) != 0) { |
strtok() is used again to subtract the next path to look for xorg.conf. The while loop iterates until all paths are searched or no config file is found
Note that xf86openConfigFile() initializes the following values that are used next at parsing by xf86readConfigFile():
configBuf = xf86confmalloc (CONFIG_BUF_LEN); configRBuf = xf86confmalloc (CONFIG_BUF_LEN); configBuf[0] = '\0'; /* sanity ... */ |
xf86HandleConfigFile() calls xf86readConfigFile() to parse xorg.conf:
if ((xf86configptr = xf86readConfigFile ()) == NULL) { |
In a Nutshell The actuall parsing of the xorg.conf file takes place in xf86HandleConfigFile(). xf86readConfigFile() initializes struct XF86ConfigRec, which stores all information from xorg.conf that is used next from the source code:
|
It allocates a XF86ConfigRec which is defined in xf86Parser.h as:
typedef struct { XF86ConfFilesPtr conf_files; XF86ConfModulePtr conf_modules; XF86ConfFlagsPtr conf_flags; XF86ConfVideoAdaptorPtr conf_videoadaptor_lst; XF86ConfModesPtr conf_modes_lst; XF86ConfMonitorPtr conf_monitor_lst; XF86ConfDevicePtr conf_device_lst; XF86ConfScreenPtr conf_screen_lst; XF86ConfInputPtr conf_input_lst; XF86ConfLayoutPtr conf_layout_lst; XF86ConfVendorPtr conf_vendor_lst; XF86ConfDRIPtr conf_dri; XF86ConfExtensionsPtr conf_extensions; char *conf_comment; } XF86ConfigRec, *XF86ConfigPtr; |
xf86readConfigFile() tries to fill XF86ConfigRec with the information from xorg.conf. For this it calls xf86getToken to read the next token from the config file:
while ((token = xf86getToken (TopLevelTab)) != EOF_TOKEN) |
TopLevelTab[] is defined as:
static xf86ConfigSymTabRec TopLevelTab[] = { {SECTION, "section"}, {-1, ""}, }; |
where xf86ConfigSymTabRec is defined as:
typedef struct { int token; /* id of the token */ char *name; /* pointer to the LOWERCASED name */ } xf86ConfigSymTabRec, *xf86ConfigSymTabPtr; |
xf86getToken() uses the configFile file descriptor returned when the configuration file opened in xf86openConfigFile(). At xf86getToken() we find:
After the xf86getToken() instruction:
c = configBuf[configPos];
and according to the initialization of configBuf[] we mentioned in xf86openConfigFile() the code that executes is:
if (!c) { char *ret; if (configFile) ret = fgets (configBuf, CONFIG_BUF_LEN - 1, configFile); |
where CONFIG_BUF_LEN is defined in scan.c as 1024.
Recall that according to the fgets man page:
char * fgets(char *restrict s, int n, FILE *restrict stream); The fgets() function reads at most one less than the number of characters specified by n from the given stream and stores them in the string s. |
The next code that executes in xf86getToken() is either:
the comment part:
if (c == '#') { do { configRBuf[i++] = (c = configBuf[configPos++]); } while ((c != '\n') && (c != '\r') && (c != '\0')); configRBuf[i] = '\0'; /* XXX no private copy. * Use xf86addComment when setting a comment. */ val.str = configRBuf; return (COMMENT); } |
or the SECTION part:
/* * ... and now we MUST have a valid token. The search is * handled later along with the pushed tokens. */ else { configRBuf[0] = c; i = 0; do { configRBuf[++i] = (c = configBuf[configPos++]);; } while ((c != ' ') && (c != '\t') && (c != '\n') && (c != '\r') && (c != '\0') && (c != '#')); --configPos; configRBuf[i] = '\0'; i = 0; } |
and
/* * Joop, at last we have to lookup the token ... */ if (tab) { i = 0; while (tab[i].token != -1) if (xf86nameCompare (configRBuf, tab[i].name) == 0) return (tab[i].token); else i++; } |
Therefore two kinds of tokens, found in xorg.conf files are used here, SECTION and COMMENT, both defined in xf86tokens.h
COMMENT is returned for an xorg.conf file comment, that is anything that starts with '#'. In that case tha conf_comment field of XF86ConfigRec is filled with the return value of xf86addComment(). As seen in xf86readConfigFile():
case COMMENT: ptr->conf_comment = xf86addComment(ptr->conf_comment, val.str); break; |
SECTION is returned for a Section..EndSection part:
Section "SectionName" SectionEntry ... EndSection |
xf86readConfigFile() handles the SECTION token using many cases:
case SECTION: . . . else if (xf86nameCompare (val.str, "pointer") == 0) { xf86conffree(val.str); val.str = NULL; HANDLE_LIST (conf_input_lst, xf86parsePointerSection, XF86ConfInputPtr); } . . . else if (xf86nameCompare (val.str, "device") == 0) { xf86conffree(val.str); val.str = NULL; HANDLE_LIST (conf_device_lst, xf86parseDeviceSection, XF86ConfDevicePtr); } . . . else if (xf86nameCompare(val.str, "inputdevice") == 0) { xf86conffree(val.str); val.str = NULL; HANDLE_LIST (conf_input_lst, xf86parseInputSection, XF86ConfInputPtr); } . . . |
The next table shows some of the functions that participate in the parsing of the various sections and the files that are found:
Section | Function | File |
"keyboard" | xf86parseKeyboardSection() | Keyboard.c |
"device" | xf86parseDeviceSection() | Device.c |
"pointer" | xf86parsePointerSection() | Pointer.c |
"ServerLayout" | xf86parseLayoutSection() | Layout.c |
"Screen" | xf86parseScreenSection() | Screen.c |
"ServerLayout" | xf86parseLayoutSection() | Layout.c |
The functions in the third column call xf86getToken() and the process the options with other routines. For instance xf86parsePointerSection() after xf86getToken() it either calls xf86addComment() if processing a comment or xf86addNewOption() if processing an option.
The parsing relies to macro HANDLE_LIST:
#define HANDLE_LIST(field,func,type)\ {\ type p = func ();\ if (p == NULL)\ {\ CLEANUP (ptr);\ return (NULL);\ }\ else\ {\ ptr->field = (type) xf86addListItem ((glp) ptr->field, (glp) p);\ }\ } |
If we take as section example the inputdevice the following code applies:
else if (xf86nameCompare(val.str, "inputdevice") == 0) { xf86conffree(val.str); val.str = NULL; HANDLE_LIST (conf_input_lst, xf86parseInputSection, XF86ConfInputPtr); } |
In that case macro HANDLE_LIST xf86parseInputSection() is executed first to fill a XF86ConfInputRec with InputSection info and then xf86addListItem() is used then to place the current XF86ConfInputRec to the next node of conf_input_lst list at XF86ConfigRec. Other structs e.g. XF86ConfMonitorRec, XF86ConfDeviceRec, etc that correspond to different section entriesare also defined in xf86Parser.h.
typedef struct { GenericListRec list; char *inp_identifier; char *inp_driver; XF86OptionPtr inp_option_lst; char *inp_comment; } XF86ConfInputRec, *XF86ConfInputPtr; |
As we saw previously with the example of inputdevice the xf86parseInputSection() applies:
XF86ConfInputPtr xf86parseInputSection (void) { int has_ident = FALSE; int token; parsePrologue (XF86ConfInputPtr, XF86ConfInputRec) while ((token = xf86getToken (InputTab)) != ENDSECTION) { switch (token) { case COMMENT: ptr->inp_comment = xf86addComment(ptr->inp_comment, val.str); break; case IDENTIFIER: if (xf86getSubToken (&(ptr->inp_comment)) != STRING) Error (QUOTE_MSG, "Identifier"); if (has_ident == TRUE) Error (MULTIPLE_MSG, "Identifier"); ptr->inp_identifier = val.str; has_ident = TRUE; break; case DRIVER: if (xf86getSubToken (&(ptr->inp_comment)) != STRING) Error (QUOTE_MSG, "Driver"); ptr->inp_driver = val.str; break; case OPTION: ptr->inp_option_lst = xf86parseOption(ptr->inp_option_lst); break; case EOF_TOKEN: Error (UNEXPECTED_EOF_MSG, NULL); break; default: Error (INVALID_KEYWORD_MSG, xf86tokenString ()); break; } } if (!has_ident) Error (NO_IDENT_MSG, NULL); #ifdef DEBUG printf ("InputDevice section parsed\n"); #endif return ptr; } |
xf86parseInputSection calls parsePrologue() to allocate a XF86ConfInputRec and place ptr to this.
#define parsePrologue(typeptr,typerec) typeptr ptr; \ if( (ptr=(typeptr)xf86confcalloc(1,sizeof(typerec))) == NULL ) { return NULL; } \ memset(ptr,0,sizeof(typerec)); |
xf86getToken() with argument InputTab is called next to acquire the next token. Then the the token is examined and if IDENTIFIER or DRIVER xf86getSubToken() is used to place the token value to the inp_identifier or inp_driver fields respectively. If the token is OPTION xf86parseOption() is used to place the possibly multiple "Option" entries an "InputDevice" Section has to inp_option_lst. The option entry is represented by struct XF86OptionRec:
typedef struct { GenericListRec list; char *opt_name; char *opt_val; int opt_used; char *opt_comment; } XF86OptionRec, *XF86OptionPtr; |
At this point we have advanced after the Option keyword in the InputDevice Section of the xorg.conf file.
Section "InputDevice" . . . Option "Device" "/dev/input/mice" . . . EndSection |
xf86parseOption() uses xf86getSubToken(), an encapsulating form of xf86getToken(), first to set val.str to the option name (e.g. "Device"), then saves the val.str value to the 'name' variable and then xf86getSubToken() is used another time to set val.str to the option value (e.g. "/dev/input/mice"). Then the option name and the option value are passed to xf86newOption() as:
option = xf86newOption(name, val.str); |
in order to create a new XF86OptionRec. The new XF86OptionRec is linked to inp_option_lst of the current XF86ConfInputRec with the following instruction of xf86parseInputSection():
case OPTION: ptr->inp_option_lst = xf86parseOption(ptr->inp_option_lst); break; |
xf86readConfigFile() returns a XF86ConfigPtr to the XF86ConfigRec that was filled.
xf86HandleConfigFile() calls xf86closeConfigFile() to close xorg.conf:
xf86closeConfigFile (); |
This closes the configuration file with a fclose() call passing the file descriptor as argument:
fclose (configFile); |
It also frees some string values like configPath:
xf86conffree (configPath); |
where xf86conffree is defined as:
#define xf86conffree free |
As seen in free man page:
"void free(void *ptr);"
"The free() function deallocates the memory allocation pointed to by ptr."