1/*
2 * Copyright 2001 Red Hat Inc., Durham, North Carolina.
3 *
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28/*
29 * Authors:
30 *   Rickard E. (Rik) Faith <faith@redhat.com>
31 *
32 */
33
34/** \file
35 * This file encapsulated all of the logging functions that are used by
36 * DMX for informational, warning, and error messages. */
37
38#ifdef HAVE_DMX_CONFIG_H
39#include <dmx-config.h>
40#endif
41
42#include "dmx.h"
43#include "dmxlog.h"
44#include "dmxinput.h"
45#include <X11/extensions/XI.h>
46#include <X11/extensions/XIproto.h>
47
48static dmxLogLevel dmxCurrentLogLevel = dmxDebug;
49
50/** Set the default level for logging to #dmxLogLevel.  Returns the
51 * previous log level. */
52dmxLogLevel dmxSetLogLevel(dmxLogLevel newLevel)
53{
54    dmxLogLevel oldLevel = dmxCurrentLogLevel;
55    if (newLevel > dmxFatal) newLevel = dmxFatal;
56    dmxCurrentLogLevel = newLevel;
57    return oldLevel;
58}
59
60/** Returns the log level set by #dmxLogLevel. */
61dmxLogLevel dmxGetLogLevel(void)
62{
63    return dmxCurrentLogLevel;
64}
65
66#ifdef DMX_LOG_STANDALONE
67/* When using this file as part of a stand-alone (i.e., non-X-Server
68 * program, then the ultimate output routines have to be defined.  */
69
70/** Provide an ErrorF function when used stand-alone. */
71void ErrorF(const char *format, ...)
72{
73    va_list args;
74
75    va_start(args, format);
76    vfprintf(stderr, format, args); /* RATS: We assume the format string
77                                     * is trusted, since it is always
78                                     * from a log message in our code. */
79    va_end(args);
80}
81
82/** Provide an VFatalError function when used stand-alone. */
83static void VFatalError(const char *format, va_list args)
84{
85    vfprintf(stderr, format, args); /* RATS: We assume the format string
86                                     * is trusted, since it is always
87                                     * from a log message in our code. */
88    exit(1);
89}
90
91/** Provide an VErrorF function when used stand-alone. */
92void VErrorF(const char *format, va_list args)
93{
94    vfprintf(stderr, format, args); /* RATS: We assume the format string
95                                     * is trusted, since it is always
96                                     * from a log message in our code. */
97}
98#else
99/** This function was removed between XFree86 4.3.0 and XFree86 4.4.0. */
100extern void AbortServer(void);
101static void VFatalError(const char *format, va_list args)
102{
103    VErrorF(format, args);
104    ErrorF("\n");
105#ifdef DDXOSFATALERROR
106    OsVendorFatalError();
107#endif
108    AbortServer();
109    /*NOTREACHED*/
110}
111#endif
112
113/* Prints a consistent header for each line. */
114static void dmxHeader(dmxLogLevel logLevel, DMXInputInfo *dmxInput,
115                      DMXScreenInfo *dmxScreen)
116{
117    const char *type = "??";
118
119    switch (logLevel) {
120    case dmxDebug:   type = ".."; break;
121    case dmxInfo:    type = "II"; break;
122    case dmxWarning: type = "**"; break;
123    case dmxError:   type = "!!"; break;
124    case dmxFatal:   type = "Fatal Error"; break;
125    }
126
127    if (dmxInput && dmxScreen) {
128        ErrorF("(%s) dmx[i%d/%s;o%d/%s]: ", type,
129               dmxInput->inputIdx, dmxInput->name,
130               dmxScreen->index, dmxScreen->name);
131    } else if (dmxScreen) {
132        ErrorF("(%s) dmx[o%d/%s]: ", type,
133               dmxScreen->index, dmxScreen->name);
134    } else if (dmxInput) {
135        const char *pt = strchr(dmxInput->name, ',');
136        int        len = (pt
137                          ? (size_t)(pt-dmxInput->name)
138                          : strlen(dmxInput->name));
139
140        ErrorF("(%s) dmx[i%d/%*.*s]: ", type,
141               dmxInput->inputIdx, len, len, dmxInput->name);
142    } else {
143        ErrorF("(%s) dmx: ", type);
144    }
145}
146
147/* Prints the error message with the appropriate low-level X output
148 * routine. */
149static void dmxMessage(dmxLogLevel logLevel, const char *format, va_list args)
150{
151    if (logLevel == dmxFatal || logLevel >= dmxCurrentLogLevel) {
152        if (logLevel == dmxFatal) VFatalError(format, args);
153        else VErrorF(format, args);
154    }
155}
156
157/** Log the specified message at the specified \a logLevel.  \a format
158 * can be a printf-like format expression. */
159void dmxLog(dmxLogLevel logLevel, const char *format, ...)
160{
161    va_list args;
162
163    dmxHeader(logLevel, NULL, NULL);
164    va_start(args, format);
165    dmxMessage(logLevel, format, args);
166    va_end(args);
167}
168
169/** Continue a log message without printing the message prefix. */
170void dmxLogCont(dmxLogLevel logLevel, const char *format, ...)
171{
172    va_list args;
173
174    va_start(args, format);
175    dmxMessage(logLevel, format, args);
176    va_end(args);
177}
178
179#ifndef DMX_LOG_STANDALONE
180/** Log an informational message (at level #dmxInfo) related to ouput.
181 * The message prefix will contain backend information from \a
182 * dmxScreen. */
183void dmxLogOutput(DMXScreenInfo *dmxScreen, const char *format, ...)
184{
185    va_list args;
186
187    dmxHeader(dmxInfo, NULL, dmxScreen);
188    va_start(args, format);
189    dmxMessage(dmxInfo, format, args);
190    va_end(args);
191}
192
193/** Continue a message related to output without printing the message
194 * prefix. */
195void dmxLogOutputCont(DMXScreenInfo *dmxScreen, const char *format, ...)
196{
197    va_list args;
198
199    va_start(args, format);
200    dmxMessage(dmxInfo, format, args);
201    va_end(args);
202}
203
204/** Log a warning message (at level #dmxWarning) related to output.
205 * The message prefix will contain backend information from \a
206 * dmxScreen. */
207void dmxLogOutputWarning(DMXScreenInfo *dmxScreen, const char *format, ...)
208{
209    va_list args;
210
211    dmxHeader(dmxWarning, NULL, dmxScreen);
212    va_start(args, format);
213    dmxMessage(dmxWarning, format, args);
214    va_end(args);
215}
216
217/** Log an informational message (at level #dmxInfo) related to input.
218 * The message prefix will contain information from \a dmxInput. */
219void dmxLogInput(DMXInputInfo *dmxInput, const char *format, ...)
220{
221    va_list args;
222
223    dmxHeader(dmxInfo, dmxInput, NULL);
224    va_start(args, format);
225    dmxMessage(dmxInfo, format, args);
226    va_end(args);
227}
228
229/** Continue a message related to input without printing the message
230 * prefix. */
231void dmxLogInputCont(DMXInputInfo *dmxInput, const char *format, ...)
232{
233    va_list args;
234
235    va_start(args, format);
236    dmxMessage(dmxInfo, format, args);
237    va_end(args);
238}
239
240/** Print \a argc messages, each describing an element in \a argv.  This
241 * is maingly for debugging purposes. */
242void dmxLogArgs(dmxLogLevel logLevel, int argc, char **argv)
243{
244    int i;
245    for (i = 0; i < argc; i++)
246        dmxLog(logLevel, "   Arg[%d] = \"%s\"\n", i, argv[i]);
247}
248
249/** Print messages at level #dmxInfo describing the visuals in \a vi. */
250void dmxLogVisual(DMXScreenInfo *dmxScreen, XVisualInfo *vi, int defaultVisual)
251{
252    const char  *class = "Unknown";
253
254    switch (vi->class) {
255    case StaticGray:  class = "StaticGray "; break;
256    case GrayScale:   class = "GrayScale  "; break;
257    case StaticColor: class = "StaticColor"; break;
258    case PseudoColor: class = "PseudoColor"; break;
259    case TrueColor:   class = "TrueColor  "; break;
260    case DirectColor: class = "DirectColor"; break;
261    }
262
263    if (dmxScreen) {
264        dmxLogOutput(dmxScreen,
265                     "0x%02x %s %2db %db/rgb %3d 0x%04x 0x%04x 0x%04x%s\n",
266                     vi->visualid, class, vi->depth, vi->bits_per_rgb,
267                     vi->colormap_size,
268                     vi->red_mask, vi->green_mask, vi->blue_mask,
269                     defaultVisual ? " *" : "");
270    } else {
271        dmxLog(dmxInfo,
272               "  0x%02x %s %2db %db/rgb %3d 0x%04x 0x%04x 0x%04x%s\n",
273               vi->visualid, class, vi->depth, vi->bits_per_rgb,
274               vi->colormap_size,
275               vi->red_mask, vi->green_mask, vi->blue_mask,
276               defaultVisual ? " *" : "");
277    }
278}
279
280/** Translate a (normalized) XInput event \a type into a human-readable
281 * string. */
282const char *dmxXInputEventName(int type)
283{
284    switch (type) {
285    case XI_DeviceValuator:          return "XI_DeviceValuator";
286    case XI_DeviceKeyPress:          return "XI_DeviceKeyPress";
287    case XI_DeviceKeyRelease:        return "XI_DeviceKeyRelease";
288    case XI_DeviceButtonPress:       return "XI_DeviceButtonPress";
289    case XI_DeviceButtonRelease:     return "XI_DeviceButtonRelease";
290    case XI_DeviceMotionNotify:      return "XI_DeviceMotionNotify";
291    case XI_DeviceFocusIn:           return "XI_DeviceFocusIn";
292    case XI_DeviceFocusOut:          return "XI_DeviceFocusOut";
293    case XI_ProximityIn:             return "XI_ProximityIn";
294    case XI_ProximityOut:            return "XI_ProximityOut";
295    case XI_DeviceStateNotify:       return "XI_DeviceStateNotify";
296    case XI_DeviceMappingNotify:     return "XI_DeviceMappingNotify";
297    case XI_ChangeDeviceNotify:      return "XI_ChangeDeviceNotify";
298    case XI_DeviceKeystateNotify:    return "XI_DeviceKeystateNotify";
299    case XI_DeviceButtonstateNotify: return "XI_DeviceButtonstateNotify";
300    default:                         return "unknown";
301    }
302}
303
304#endif
305
306/** Translate an event \a type into a human-readable string. */
307const char *dmxEventName(int type)
308{
309    switch (type) {
310    case KeyPress:         return "KeyPress";
311    case KeyRelease:       return "KeyRelease";
312    case ButtonPress:      return "ButtonPress";
313    case ButtonRelease:    return "ButtonRelease";
314    case MotionNotify:     return "MotionNotify";
315    case EnterNotify:      return "EnterNotify";
316    case LeaveNotify:      return "LeaveNotify";
317    case FocusIn:          return "FocusIn";
318    case FocusOut:         return "FocusOut";
319    case KeymapNotify:     return "KeymapNotify";
320    case Expose:           return "Expose";
321    case GraphicsExpose:   return "GraphicsExpose";
322    case NoExpose:         return "NoExpose";
323    case VisibilityNotify: return "VisibilityNotify";
324    case CreateNotify:     return "CreateNotify";
325    case DestroyNotify:    return "DestroyNotify";
326    case UnmapNotify:      return "UnmapNotify";
327    case MapNotify:        return "MapNotify";
328    case MapRequest:       return "MapRequest";
329    case ReparentNotify:   return "ReparentNotify";
330    case ConfigureNotify:  return "ConfigureNotify";
331    case ConfigureRequest: return "ConfigureRequest";
332    case GravityNotify:    return "GravityNotify";
333    case ResizeRequest:    return "ResizeRequest";
334    case CirculateNotify:  return "CirculateNotify";
335    case CirculateRequest: return "CirculateRequest";
336    case PropertyNotify:   return "PropertyNotify";
337    case SelectionClear:   return "SelectionClear";
338    case SelectionRequest: return "SelectionRequest";
339    case SelectionNotify:  return "SelectionNotify";
340    case ColormapNotify:   return "ColormapNotify";
341    case ClientMessage:    return "ClientMessage";
342    case MappingNotify:    return "MappingNotify";
343    default:               return "<unknown>";
344    }
345}
346
347