xenocara/app/ssh-askpass/x11-ssh-askpass.c
matthieu f64b917b27 Make ssh-ask-pass Xinerama aware (also works with XRandR thanks to
its pseudo-Xinerama emulation). ok stsp@
2010-01-22 11:24:31 +00:00

1583 lines
47 KiB
C

/* x11-ssh-askpass.c: A generic X11-based password dialog for OpenSSH.
* created 1999-Nov-17 03:40 Jim Knoble <jmknoble@jmknoble.cx>
* autodate: 2001-Feb-14 04:00
*
* by Jim Knoble <jmknoble@jmknoble.cx>
* Copyright (C) 1999,2000,2001 Jim Knoble
*
* Disclaimer:
*
* The software is provided "as is", without warranty of any kind,
* express or implied, including but not limited to the warranties of
* merchantability, fitness for a particular purpose and
* noninfringement. In no event shall the author(s) be liable for any
* claim, damages or other liability, whether in an action of
* contract, tort or otherwise, arising from, out of or in connection
* with the software or the use or other dealings in the software.
*
* Portions of this code are distantly derived from code in xscreensaver
* by Jamie Zawinski <jwz@jwz.org>. That code says:
*
* --------8<------------------------------------------------8<--------
* xscreensaver, Copyright (c) 1991-1999 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
* --------8<------------------------------------------------8<--------
*
* The remainder of this code falls under the same permissions and
* provisions as those of the xscreensaver code.
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* For (get|set)rlimit() ... */
#include <sys/time.h>
#include <sys/resource.h>
/* ... end */
/* For (get|set)rlimit(), sleep(), and getpid() ... */
#include <unistd.h>
/* ... end */
/* For errno ... */
#include <errno.h>
/* ... end */
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/Xos.h>
#include <X11/extensions/Xinerama.h>
#include "dynlist.h"
#include "drawing.h"
#include "resources.h"
#include "x11-ssh-askpass.h"
#undef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
char *progname = NULL;
char *progclass = NULL;
XrmDatabase db = 0;
static char *defaults[] = {
#include "SshAskpass_ad.h"
0
};
void outOfMemory(AppInfo *app, int line)
{
fprintf(stderr, "%s[%ld]: Aaahhh! I ran out of memory at line %d.\n",
app->appName, (long) app->pid, line);
exit(EXIT_STATUS_NO_MEMORY);
}
void freeIf(void *p)
{
if (p) {
free(p);
}
}
void freeFontIf(AppInfo *app, XFontStruct *f)
{
if (f) {
XFreeFont(app->dpy, f);
}
}
XFontStruct *getFontResource(AppInfo *app, char *instanceName, char *className)
{
char *fallbackFont = "fixed";
XFontStruct *f = NULL;
char *s = get_string_resource(instanceName, className);
f = XLoadQueryFont(app->dpy, (s ? s : fallbackFont));
if (!f) {
f = XLoadQueryFont(app->dpy, fallbackFont);
}
if (s) {
free(s);
}
return(f);
}
char *getStringResourceWithDefault(char *instanceName, char *className,
char *defaultText)
{
char *s = get_string_resource(instanceName, className);
if (!s) {
if (!defaultText) {
s = strdup("");
} else {
s = strdup(defaultText);
}
}
return(s);
}
unsigned int getUnsignedIntegerResource(AppInfo *app, char *instanceName,
char *className,
unsigned int defaultValue)
{
int n;
unsigned int value;
char c;
char *s = get_string_resource(instanceName, className);
char *cp = s;
if (NULL == s) {
return(defaultValue);
}
while ((*cp) && isspace(*cp)) {
/* Skip whitespace. */
cp++;
}
if (*cp) {
if (('0' == cp[0]) && cp[1]) {
if (('x' == cp[1]) || ('X' == cp[1])) {
/* Hex */
n = sscanf(cp + 2, "%x %c", &value, &c);
} else {
/* Octal */
n = sscanf(cp + 1, "%o %c", &value, &c);
}
if (1 == n) {
free(s);
return(value);
}
} else {
/* Unsigned Decimal */
n = sscanf(cp, "%u %c", &value, &c);
if (1 == n) {
free(s);
return(value);
}
}
}
/* If we get here, no conversion succeeded. */
fprintf(stderr, "%s[%ld]: invalid value '%s' for %s.\n",
app->appName, (long) app->pid, s, instanceName);
free(s);
return(defaultValue);
}
/* Default resolution is 75 dots/inch. 1 in = 2.54 cm. */
#define DefaultResolution ((75 * 10000) / 254)
long getResolutionResource(AppInfo *app, char *instanceName, char *className,
char *defaultResolutionSpec)
{
char units[3];
char *s;
int n;
long resolution;
unsigned int i;
memset(units, 0, sizeof(units));
s = getStringResourceWithDefault(instanceName, className,
defaultResolutionSpec);
/* NOTE: The width of the %s format must be one less than
* the length of the units[] array above!
*/
n = sscanf(s, "%ld / %2s", &resolution, units);
if (n != 2) {
fprintf(stderr, "%s[%ld]: invalid value '%s' for %s.\n",
app->appName, (long) app->pid, s, instanceName);
resolution = DefaultResolution;
} else {
if (resolution < 0) {
/* Resolution specifications should not be negative. */
resolution = -(resolution);
}
for (i = 0; i < (sizeof(units) - 1); i++) {
units[i] = tolower(units[i]);
}
if ((0 == strcmp(units, "in")) ||
(0 == strcmp(units, "i")) ||
(0 == strcmp(units, "\""))) {
/* dots/inch */
resolution = resolution * 10000 / 254;
} else if ((0 == strcmp(units, "m")) ||
(0 == strcmp(units, "me"))) {
/* dots/meter; no conversion necessary */
;
} else {
/* some unit we don't recognize; cringe and stare at the floor */
resolution = DefaultResolution;
}
}
return(resolution);
}
#undef DefaultResolution
void calcTextObjectExtents(TextObject *t, XFontStruct *font) {
if ((!t) || (!(t->text))) {
return;
}
t->textLength = strlen(t->text);
XTextExtents(font, t->text, t->textLength, &(t->direction),
&(t->ascent), &(t->descent), &(t->overall));
}
void calcLabelTextExtents(LabelInfo *label)
{
TextObject *t;
int first = 1;
if ((!label) || (!(label->fullText)) || (!(label->font))) {
return;
}
t = label->multiText;
while (NULL != t) {
if (first) {
calcTextObjectExtents(t, label->font);
first = 0;
} else
calcTextObjectExtents(t, label->fixedFont);
label->w.height += (t->ascent + t->descent);
if (label->w.width < t->overall.width) {
label->w.width = t->overall.width;
}
t = t->next;
}
}
void calcTotalButtonExtents(ButtonInfo *button)
{
if (!button) {
return;
}
button->w3.w.width = (button->w3.interiorWidth +
(2 * button->w3.shadowThickness));
button->w3.w.width += (2 * button->w3.borderWidth);
button->w3.w.height = (button->w3.interiorHeight +
(2 * button->w3.shadowThickness));
button->w3.w.height += (2 * button->w3.borderWidth);
}
void calcButtonExtents(ButtonInfo *button)
{
if (!button) {
return;
}
calcLabelTextExtents(&(button->label));
button->w3.interiorWidth = (button->label.w.width +
(2 * button->w3.horizontalSpacing));
button->w3.interiorHeight = (button->label.w.height +
(2 * button->w3.verticalSpacing));
calcTotalButtonExtents(button);
}
void balanceButtonExtents(ButtonInfo *button1, ButtonInfo *button2)
{
if ((!button1) || (!button2)) {
return;
}
button1->w3.interiorWidth = button2->w3.interiorWidth =
MAX(button1->w3.interiorWidth, button2->w3.interiorWidth);
button1->w3.interiorHeight = button2->w3.interiorHeight =
MAX(button1->w3.interiorHeight, button2->w3.interiorHeight);
calcTotalButtonExtents(button1);
calcTotalButtonExtents(button2);
}
void calcButtonLabelPosition(ButtonInfo *button)
{
if (!button) {
return;
}
button->label.w.x = button->w3.w.x +
((button->w3.w.width - button->label.w.width) / 2);
button->label.w.y = button->w3.w.y +
((button->w3.w.height - button->label.w.height) / 2);
}
Dimension scaleXDimension(AppInfo *app, Dimension unscaled)
{
Dimension scaled;
if (((app->defaultXResolution < app->xResolution) &&
((app->defaultXResolution + app->xFuzz) < app->xResolution)) ||
((app->xResolution < app->defaultXResolution) &&
((app->xResolution + app->xFuzz) < app->defaultXResolution))) {
scaled = (unscaled * app->xResolution) / app->defaultXResolution;
} else {
scaled = unscaled;
}
return(scaled);
}
Dimension scaleYDimension(AppInfo *app, Dimension unscaled)
{
Dimension scaled;
if (((app->defaultYResolution < app->yResolution) &&
((app->defaultYResolution + app->yFuzz) < app->yResolution)) ||
((app->yResolution < app->defaultYResolution) &&
((app->yResolution + app->yFuzz) < app->defaultYResolution))) {
scaled = (unscaled * app->yResolution) / app->defaultYResolution;
} else {
scaled = unscaled;
}
return(scaled);
}
/* Assumes 's' is non-NULL. */
TextObject *createTextObject(AppInfo *app, char *s)
{
TextObject *t = malloc(sizeof(*t));
if (NULL == t) {
outOfMemory(app, __LINE__);
}
memset(t, 0, sizeof(*t));
if (('\n' == *s) || ('\0' == *s)) {
t->text = " ";
} else {
t->text = s;
}
return(t);
}
/* Assumes 'label' object exists and is zeroed. */
void createLabel(AppInfo *app, char *text, LabelInfo *label)
{
char *substring;
TextObject *t;
if ((!app) || (!text)) {
return;
}
label->fullText = strdup(text);
label->multiText = createTextObject(app, label->fullText);
t = label->multiText;
substring = strchr(label->fullText, '\n');
while (NULL != substring) {
*(substring++) = '\0';
t->next = createTextObject(app, substring);
if (t->next) {
t = t->next;
}
substring = strchr(substring, '\n');
}
}
void createDialog(AppInfo *app)
{
DialogInfo *d;
char *labelText;
if (app->dialog) {
return;
}
d = malloc(sizeof(*d));
if (NULL == d) {
outOfMemory(app, __LINE__);
}
memset(d, 0, sizeof(*d));
app->grabKeyboard =
get_boolean_resource("grabKeyboard", "GrabKeyboard", True);
app->grabPointer =
get_boolean_resource("grabPointer", "GrabPointer", False);
app->grabServer =
get_boolean_resource("grabServer", "GrabServer", False);
/* inputTimeout resource specified in seconds for easy human interface.
* Convert to milliseconds here.
*/
app->inputTimeout = (unsigned long) 1000 *
getUnsignedIntegerResource(app, "inputTimeout", "InputTimeout", 0);
app->defaultXResolution =
getResolutionResource(app, "defaultXResolution", "DefaultXResolution",
"75/in");
app->defaultYResolution =
getResolutionResource(app, "defaultYResolution", "DefaultYResolution",
"75/in");
app->xFuzz =
getResolutionResource(app, "xResolutionFuzz", "XResolutionFuzz", "20/in");
app->yFuzz =
getResolutionResource(app, "yResolutionFuzz", "YResolutionFuzz", "20/in");
d->title =
getStringResourceWithDefault("dialog.title", "Dialog.Title",
"OpenSSH Authentication Passphrase Request");
d->w3.w.foreground =
get_pixel_resource("foreground", "Foreground",
app->dpy, app->colormap, app->black);
d->w3.w.background =
get_pixel_resource("background", "Background",
app->dpy, app->colormap, app->white);
d->w3.topShadowColor =
get_pixel_resource("topShadowColor", "TopShadowColor",
app->dpy, app->colormap, app->white);
d->w3.bottomShadowColor =
get_pixel_resource("bottomShadowColor", "BottomShadowColor",
app->dpy, app->colormap, app->black);
d->w3.shadowThickness =
get_integer_resource("shadowThickness", "ShadowThickness", 3);
d->w3.borderColor =
get_pixel_resource("borderColor", "BorderColor",
app->dpy, app->colormap, app->black);
d->w3.borderWidth =
get_integer_resource("borderWidth", "BorderWidth", 1);
d->w3.horizontalSpacing = scaleXDimension(app,
get_integer_resource("horizontalSpacing", "Spacing", 5));
d->w3.verticalSpacing = scaleYDimension(app,
get_integer_resource("verticalSpacing", "Spacing", 6));
if (2 == app->argc) {
labelText = strdup(app->argv[1]);
} else {
labelText =
getStringResourceWithDefault("dialog.label", "Dialog.Label",
"Please enter your authentication passphrase:");
}
createLabel(app, labelText, &(d->label));
freeIf(labelText);
d->label.font = getFontResource(app, "dialog.font", "Dialog.Font");
d->label.fixedFont = getFontResource(app, "dialog.fixedFont",
"Dialog.FixedFont");
calcLabelTextExtents(&(d->label));
d->label.w.foreground = d->w3.w.foreground;
d->label.w.background = d->w3.w.background;
d->okButton.w3.w.foreground =
get_pixel_resource("okButton.foreground", "Button.Foreground",
app->dpy, app->colormap, app->black);
d->okButton.w3.w.background =
get_pixel_resource("okButton.background", "Button.Background",
app->dpy, app->colormap, app->white);
d->okButton.w3.topShadowColor =
get_pixel_resource("okButton.topShadowColor", "Button.TopShadowColor",
app->dpy, app->colormap, app->white);
d->okButton.w3.bottomShadowColor =
get_pixel_resource("okButton.bottomShadowColor",
"Button.BottomShadowColor",
app->dpy, app->colormap, app->black);
d->okButton.w3.shadowThickness =
get_integer_resource("okButton.shadowThickness",
"Button.ShadowThickness", 2);
d->okButton.w3.borderColor =
get_pixel_resource("okButton.borderColor", "Button.BorderColor",
app->dpy, app->colormap, app->black);
d->okButton.w3.borderWidth =
get_integer_resource("okButton.borderWidth", "Button.BorderWidth", 1);
d->okButton.w3.horizontalSpacing = scaleXDimension(app,
get_integer_resource("okButton.horizontalSpacing", "Button.Spacing", 4));
d->okButton.w3.verticalSpacing = scaleYDimension(app,
get_integer_resource("okButton.verticalSpacing", "Button.Spacing", 2));
labelText =
getStringResourceWithDefault("okButton.label", "Button.Label", "OK");
createLabel(app, labelText, &(d->okButton.label));
freeIf(labelText);
d->okButton.label.font =
getFontResource(app, "okButton.font", "Button.Font");
calcButtonExtents(&(d->okButton));
d->okButton.label.w.foreground = d->okButton.w3.w.foreground;
d->okButton.label.w.background = d->okButton.w3.w.background;
d->cancelButton.w3.w.foreground =
get_pixel_resource("cancelButton.foreground", "Button.Foreground",
app->dpy, app->colormap, app->black);
d->cancelButton.w3.w.background =
get_pixel_resource("cancelButton.background", "Button.Background",
app->dpy, app->colormap, app->white);
d->cancelButton.w3.topShadowColor =
get_pixel_resource("cancelButton.topShadowColor",
"Button.TopShadowColor",
app->dpy, app->colormap, app->white);
d->cancelButton.w3.bottomShadowColor =
get_pixel_resource("cancelButton.bottomShadowColor",
"Button.BottomShadowColor",
app->dpy, app->colormap, app->black);
d->cancelButton.w3.shadowThickness =
get_integer_resource("cancelButton.shadowThickness",
"Button.ShadowThickness", 2);
d->cancelButton.w3.borderColor =
get_pixel_resource("cancelButton.borderColor", "Button.BorderColor",
app->dpy, app->colormap, app->black);
d->cancelButton.w3.borderWidth =
get_integer_resource("cancelButton.borderWidth", "Button.BorderWidth",
1);
d->cancelButton.w3.horizontalSpacing = scaleXDimension(app,
get_integer_resource("cancelButton.horizontalSpacing", "Button.Spacing",
4));
d->cancelButton.w3.verticalSpacing = scaleYDimension(app,
get_integer_resource("cancelButton.verticalSpacing", "Button.Spacing",
2));
labelText =
getStringResourceWithDefault("cancelButton.label", "Button.Label",
"Cancel");
createLabel(app, labelText, &(d->cancelButton.label));
freeIf(labelText);
d->cancelButton.label.font =
getFontResource(app, "cancelButton.font", "Button.Font");
calcButtonExtents(&(d->cancelButton));
d->cancelButton.label.w.foreground = d->cancelButton.w3.w.foreground;
d->cancelButton.label.w.background = d->cancelButton.w3.w.background;
balanceButtonExtents(&(d->okButton), &(d->cancelButton));
d->indicator.w3.w.foreground =
get_pixel_resource("indicator.foreground", "Indicator.Foreground",
app->dpy, app->colormap, app->black);
d->indicator.w3.w.background =
get_pixel_resource("indicator.background", "Indicator.Background",
app->dpy, app->colormap, app->white);
d->indicator.w3.w.width = scaleXDimension(app,
get_integer_resource("indicator.width", "Indicator.Width", 15));
d->indicator.w3.w.height = scaleYDimension(app,
get_integer_resource("indicator.height", "Indicator.Height", 7));
d->indicator.w3.topShadowColor =
get_pixel_resource("indicator.topShadowColor",
"Indicator.TopShadowColor",
app->dpy, app->colormap, app->white);
d->indicator.w3.bottomShadowColor =
get_pixel_resource("indicator.bottomShadowColor",
"Indicator.BottomShadowColor",
app->dpy, app->colormap, app->black);
d->indicator.w3.shadowThickness =
get_integer_resource("indicator.shadowThickness",
"Indicator.ShadowThickness", 2);
d->indicator.w3.borderColor =
get_pixel_resource("indicator.borderColor", "Indicator.BorderColor",
app->dpy, app->colormap, app->black);
d->indicator.w3.borderWidth =
get_integer_resource("indicator.borderWidth", "Indicator.BorderWidth",
0);
d->indicator.w3.horizontalSpacing = scaleXDimension(app,
get_integer_resource("indicator.horizontalSpacing", "Indicator.Spacing",
2));
d->indicator.w3.verticalSpacing =scaleYDimension(app,
get_integer_resource("indicator.verticalSpacing", "Indicator.Spacing",
4));
d->indicator.minimumCount =
get_integer_resource("indicator.minimumCount", "Indicator.MinimumCount",
8);
d->indicator.maximumCount =
get_integer_resource("indicator.maximumCount", "Indicator.MaximumCount",
24);
d->indicator.w3.interiorWidth = d->indicator.w3.w.width;
d->indicator.w3.interiorHeight = d->indicator.w3.w.height;
d->indicator.w3.w.width += (2 * d->indicator.w3.shadowThickness);
d->indicator.w3.w.width += (2 * d->indicator.w3.borderWidth);
d->indicator.w3.w.height += (2 * d->indicator.w3.shadowThickness);
d->indicator.w3.w.height += (2 * d->indicator.w3.borderWidth);
{
/* Make sure the indicators can all fit on the screen.
* 80% of the screen width seems fine.
*/
Dimension maxWidth = (app->screen_width * 8 / 10);
Dimension extraSpace = ((2 * d->w3.horizontalSpacing) +
(2 * d->w3.shadowThickness));
if (d->indicator.maximumCount < 8) {
d->indicator.maximumCount = 8;
}
if (((d->indicator.maximumCount * d->indicator.w3.w.width) +
((d->indicator.maximumCount - 1) *
d->indicator.w3.horizontalSpacing) + extraSpace) > maxWidth) {
d->indicator.maximumCount =
((maxWidth - extraSpace - d->indicator.w3.w.width) /
(d->indicator.w3.w.width + d->indicator.w3.horizontalSpacing))
+ 1;
}
if (d->indicator.minimumCount <= 6) {
d->indicator.minimumCount = 6;
}
if (d->indicator.minimumCount > d->indicator.maximumCount) {
d->indicator.minimumCount = d->indicator.maximumCount;
}
}
{
/* Calculate the width and horizontal position of things. */
Dimension labelAreaWidth;
Dimension buttonAreaWidth;
Dimension indicatorAreaWidth;
Dimension extraIndicatorSpace;
Dimension singleIndicatorSpace;
Dimension interButtonSpace;
Dimension w;
Position leftX;
int i;
labelAreaWidth = d->label.w.width + (2 * d->w3.horizontalSpacing);
buttonAreaWidth = ((3 * d->w3.horizontalSpacing) +
d->okButton.w3.w.width +
d->cancelButton.w3.w.width);
w = MAX(labelAreaWidth, buttonAreaWidth);
extraIndicatorSpace = ((2 * d->w3.horizontalSpacing) +
d->indicator.w3.w.width);
singleIndicatorSpace = (d->indicator.w3.w.width +
d->indicator.w3.horizontalSpacing);
d->indicator.count = ((w - extraIndicatorSpace) / singleIndicatorSpace);
d->indicator.current = 0;
d->indicator.count++; /* For gatepost indicator in extra space. */
if (((w - extraIndicatorSpace) % singleIndicatorSpace) >
(singleIndicatorSpace / 2)) {
d->indicator.count++;
}
if (d->indicator.count < d->indicator.minimumCount) {
d->indicator.count = d->indicator.minimumCount;
}
if (d->indicator.count > d->indicator.maximumCount) {
d->indicator.count = d->indicator.maximumCount;
}
indicatorAreaWidth = ((singleIndicatorSpace * (d->indicator.count - 1)) +
extraIndicatorSpace);
d->w3.interiorWidth = MAX(w, indicatorAreaWidth);
d->w3.w.width = d->w3.interiorWidth + (2 * d->w3.shadowThickness);
leftX = (d->w3.w.width - d->label.w.width) / 2;
d->label.w.x = leftX;
leftX = ((d->w3.w.width -
(d->indicator.count * d->indicator.w3.w.width) -
((d->indicator.count - 1) * d->indicator.w3.horizontalSpacing))
/ 2);
{
int n = d->indicator.count * sizeof(IndicatorElement);
d->indicators = malloc(n);
if (NULL == d->indicators) {
destroyDialog(app);
outOfMemory(app, __LINE__);
}
memset(d->indicators, 0, n);
}
d->indicators[0].parent = &(d->indicator);
d->indicators[0].w.x = d->indicator.w3.w.x = leftX;
d->indicators[0].w.width = d->indicator.w3.w.width;
d->indicators[0].isLit = False;
for (i = 1; i < d->indicator.count; i++) {
d->indicators[i].parent = &(d->indicator);
d->indicators[i].w.x = (d->indicators[i - 1].w.x +
d->indicator.w3.w.width +
d->indicator.w3.horizontalSpacing);
d->indicators[i].w.width = d->indicator.w3.w.width;
d->indicators[i].isLit = False;
}
interButtonSpace = ((d->w3.interiorWidth - d->okButton.w3.w.width -
d->cancelButton.w3.w.width) / 3);
d->okButton.w3.w.x = interButtonSpace + d->w3.shadowThickness;
d->cancelButton.w3.w.x = (d->okButton.w3.w.x + d->okButton.w3.w.width +
interButtonSpace);
}
{
/* Calculate the height and vertical position of things. */
int i;
d->w3.interiorHeight = ((4 * d->w3.verticalSpacing) +
(2 * d->indicator.w3.verticalSpacing) +
d->label.w.height +
d->indicator.w3.w.height +
d->okButton.w3.w.height);
d->w3.w.height = d->w3.interiorHeight + (2 * d->w3.shadowThickness);
d->label.w.y = d->w3.shadowThickness + d->w3.verticalSpacing;
d->indicator.w3.w.y = (d->label.w.y + d->label.w.height +
d->w3.verticalSpacing +
d->indicator.w3.verticalSpacing);
for (i = 0; i < d->indicator.count; i++) {
d->indicators[i].w.y = d->indicator.w3.w.y;
d->indicators[i].w.height = d->indicator.w3.w.height;
}
d->okButton.w3.w.y = d->cancelButton.w3.w.y =
(d->indicator.w3.w.y + d->indicator.w3.w.height +
d->w3.verticalSpacing + d->indicator.w3.verticalSpacing);
}
calcButtonLabelPosition(&(d->okButton));
calcButtonLabelPosition(&(d->cancelButton));
d->w3.w.x = app->screen_xoffset + (app->screen_width - d->w3.w.width) / 2;
d->w3.w.y = app->screen_yoffset + (app->screen_height - d->w3.w.height) / 3;
app->dialog = d;
}
void destroyLabel(AppInfo *app, LabelInfo *label)
{
TextObject *thisTextObject;
TextObject *nextTextObject;
thisTextObject = label->multiText;
nextTextObject = thisTextObject->next;
freeIf(thisTextObject);
while (NULL != nextTextObject) {
thisTextObject = nextTextObject;
nextTextObject = thisTextObject->next;
freeIf(thisTextObject);
}
freeIf(label->fullText);
freeFontIf(app, label->font);
freeFontIf(app, label->fixedFont);
}
void destroyDialog(AppInfo *app)
{
DialogInfo *d = app->dialog;
freeIf(d->title);
freeIf(d->indicators);
destroyLabel(app, &(d->label));
destroyLabel(app, &(d->okButton.label));
destroyLabel(app, &(d->cancelButton.label));
XFree(d->sizeHints);
XFree(d->wmHints);
XFree(d->classHints);
XFree(d->windowName.value);
freeIf(d);
}
void createDialogWindow(AppInfo *app)
{
XSetWindowAttributes attr;
unsigned long attrMask = 0;
DialogInfo *d = app->dialog;
attr.background_pixel = d->w3.w.background;
attrMask |= CWBackPixel;
attr.border_pixel = d->w3.borderColor;
attrMask |= CWBorderPixel;
attr.cursor = None;
attrMask |= CWCursor;
attr.event_mask = app->eventMask;
attrMask |= CWEventMask;
d->dialogWindow = XCreateWindow(app->dpy, app->rootWindow,
d->w3.w.x, d->w3.w.y,
d->w3.w.width, d->w3.w.height,
d->w3.borderWidth,
DefaultDepthOfScreen(app->screen),
InputOutput,
DefaultVisualOfScreen(app->screen),
attrMask, &attr);
d->sizeHints = XAllocSizeHints();
if (!(d->sizeHints)) {
destroyDialog(app);
outOfMemory(app, __LINE__);
}
d->sizeHints->flags = 0;
d->sizeHints->flags |= PPosition;
d->sizeHints->flags |= PSize;
d->sizeHints->min_width = d->w3.w.width;
d->sizeHints->min_height = d->w3.w.height;
d->sizeHints->flags |= PMinSize;
d->sizeHints->max_width = d->w3.w.width;
d->sizeHints->max_height = d->w3.w.height;
d->sizeHints->flags |= PMaxSize;
d->sizeHints->base_width = d->w3.w.width;
d->sizeHints->base_height = d->w3.w.height;
d->sizeHints->flags |= PBaseSize;
d->wmHints = XAllocWMHints();
if (!(d->wmHints)) {
destroyDialog(app);
outOfMemory(app, __LINE__);
}
d->wmHints->flags = 0;
d->wmHints->input = True;
d->wmHints->flags |= InputHint;
d->wmHints->initial_state = NormalState;
d->wmHints->flags |= StateHint;
d->classHints = XAllocClassHint();
if (!(d->classHints)) {
destroyDialog(app);
outOfMemory(app, __LINE__);
}
d->classHints->res_name = app->appName;
d->classHints->res_class = app->appClass;
if (!XStringListToTextProperty(&(d->title), 1, &(d->windowName))) {
destroyDialog(app);
outOfMemory(app, __LINE__);
}
XSetWMProperties(app->dpy, d->dialogWindow, &(d->windowName), NULL,
app->argv, app->argc, d->sizeHints,
d->wmHints, d->classHints);
XSetTransientForHint(app->dpy, d->dialogWindow, d->dialogWindow);
app->wmDeleteWindowAtom = XInternAtom(app->dpy, "WM_DELETE_WINDOW", False);
XSetWMProtocols(app->dpy, d->dialogWindow, &(app->wmDeleteWindowAtom), 1);
}
void createGCs(AppInfo *app)
{
DialogInfo *d = app->dialog;
XGCValues gcv;
unsigned long gcvMask;
gcvMask = 0;
gcv.foreground = d->w3.w.background;
gcvMask |= GCForeground;
gcv.fill_style = FillSolid;
gcvMask |= GCFillStyle;
app->fillGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
gcvMask = 0;
gcv.foreground = d->w3.borderColor;
gcvMask |= GCForeground;
gcv.line_width = d->w3.borderWidth;
gcvMask |= GCLineWidth;
gcv.line_style = LineSolid;
gcvMask |= GCLineStyle;
gcv.cap_style = CapButt;
gcvMask |= GCCapStyle;
gcv.join_style = JoinMiter;
gcvMask |= GCJoinStyle;
app->borderGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
gcvMask = 0;
gcv.foreground = d->label.w.foreground;
gcvMask |= GCForeground;
gcv.background = d->label.w.background;
gcvMask |= GCBackground;
gcv.font = d->label.font->fid;
gcvMask |= GCFont;
app->textGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
gcvMask = 0;
gcv.foreground = d->indicator.w3.w.foreground;
gcvMask |= GCForeground;
gcv.fill_style = FillSolid;
gcvMask |= GCFillStyle;
app->brightGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
gcvMask = 0;
gcv.foreground = d->indicator.w3.w.background;
gcvMask |= GCForeground;
gcv.fill_style = FillSolid;
gcvMask |= GCFillStyle;
app->dimGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
}
void destroyGCs(AppInfo *app)
{
XFreeGC(app->dpy, app->fillGC);
XFreeGC(app->dpy, app->borderGC);
XFreeGC(app->dpy, app->textGC);
XFreeGC(app->dpy, app->brightGC);
XFreeGC(app->dpy, app->dimGC);
}
void paintLabel(AppInfo *app, Drawable draw, LabelInfo label)
{
TextObject *t;
Position x;
Position y;
int first = 1;
if (!(label.fullText)) {
return;
}
XSetForeground(app->dpy, app->textGC, label.w.foreground);
XSetBackground(app->dpy, app->textGC, label.w.background);
XSetFont(app->dpy, app->textGC, label.font->fid);
t = label.multiText;
x = label.w.x;
y = label.w.y + t->ascent;
while (NULL != t) {
if (!first)
XSetFont(app->dpy, app->textGC, label.fixedFont->fid);
else
first = 0;
if (t->text) {
XDrawString(app->dpy, draw, app->textGC, x, y, t->text,
t->textLength);
}
y += t->descent;
t = t->next;
if (t) {
y += t->ascent;
}
}
}
void paintButton(AppInfo *app, Drawable draw, ButtonInfo button)
{
Position x;
Position y;
Dimension width;
Dimension height;
if (button.w3.borderWidth > 0) {
XSetForeground(app->dpy, app->borderGC, button.w3.borderColor);
XFillRectangle(app->dpy, draw, app->borderGC, button.w3.w.x,
button.w3.w.y, button.w3.w.width, button.w3.w.height);
}
if ((button.w3.shadowThickness <= 0) && (button.pressed)) {
Pixel tmp = button.w3.w.background;
button.w3.w.background = button.w3.w.foreground;
button.w3.w.foreground = tmp;
tmp = button.label.w.background;
button.label.w.background = button.label.w.foreground;
button.label.w.foreground = tmp;
}
x = (button.w3.w.x + button.w3.borderWidth);
y = (button.w3.w.y + button.w3.borderWidth);
width = (button.w3.w.width - (2 * button.w3.borderWidth));
height = (button.w3.w.height - (2 * button.w3.borderWidth));
if ((button.w3.shadowThickness > 0) && (button.pressed)) {
XSetForeground(app->dpy, app->fillGC, button.w3.topShadowColor);
} else {
XSetForeground(app->dpy, app->fillGC, button.w3.w.background);
}
XFillRectangle(app->dpy, draw, app->fillGC, x, y, width, height);
if (button.w3.shadowThickness > 0) {
if (button.pressed) {
draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
button.w3.shadowThickness,
button.w3.bottomShadowColor,
button.w3.topShadowColor);
} else {
draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
button.w3.shadowThickness,
button.w3.topShadowColor,
button.w3.bottomShadowColor);
}
}
if ((button.w3.shadowThickness > 0) && (button.pressed)) {
Dimension pressedAdjustment;
pressedAdjustment = button.w3.shadowThickness / 2;
if (pressedAdjustment < 1) {
pressedAdjustment = 1;
}
x = button.label.w.x;
y = button.label.w.y;
button.label.w.x += pressedAdjustment;
button.label.w.y += pressedAdjustment;
paintLabel(app, draw, button.label);
button.label.w.x = x;
button.label.w.y = y;
} else {
paintLabel(app, draw, button.label);
}
if ((button.w3.shadowThickness <= 0) && (button.pressed)) {
Pixel tmp = button.w3.w.background;
button.w3.w.background = button.w3.w.foreground;
button.w3.w.foreground = tmp;
tmp = button.label.w.background;
button.label.w.background = button.label.w.foreground;
button.label.w.foreground = tmp;
}
}
void paintIndicator(AppInfo *app, Drawable draw, IndicatorElement indicator)
{
Position x;
Position y;
Dimension width;
Dimension height;
GC gc = app->dimGC;
if (indicator.parent->w3.borderWidth > 0) {
XSetForeground(app->dpy, app->borderGC,
indicator.parent->w3.borderColor);
XFillRectangle(app->dpy, draw, app->borderGC, indicator.w.x,
indicator.w.y, indicator.w.width, indicator.w.height);
}
if (indicator.isLit) {
gc = app->brightGC;
}
x = (indicator.w.x + indicator.parent->w3.borderWidth);
y = (indicator.w.y + indicator.parent->w3.borderWidth);
width = (indicator.w.width - (2 * indicator.parent->w3.borderWidth));
height = (indicator.w.height - (2 * indicator.parent->w3.borderWidth));
XFillRectangle(app->dpy, draw, gc, x, y, width, height);
if (indicator.parent->w3.shadowThickness > 0) {
draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
indicator.parent->w3.shadowThickness,
indicator.parent->w3.bottomShadowColor,
indicator.parent->w3.topShadowColor);
}
}
void updateIndicatorElement(AppInfo *app, int i)
{
DialogInfo *d = app->dialog;
d->indicators[i].isLit = !(d->indicators[i].isLit);
paintIndicator(app, d->dialogWindow, d->indicators[i]);
}
void updateIndicators(AppInfo *app, int condition)
{
DialogInfo *d = app->dialog;
if (condition > 0) {
/* Move forward one. */
updateIndicatorElement(app, d->indicator.current);
if (d->indicator.current < (d->indicator.count - 1)) {
(d->indicator.current)++;
} else {
d->indicator.current = 0;
}
} else if (condition < 0) {
/* Move backward one. */
if (d->indicator.current > 0) {
(d->indicator.current)--;
} else {
d->indicator.current = d->indicator.count - 1;
}
updateIndicatorElement(app, d->indicator.current);
} else {
/* Erase them all. */
int i;
for (i = 0; i < d->indicator.count; i++) {
d->indicators[i].isLit = False;
paintIndicator(app, d->dialogWindow, d->indicators[i]);
}
d->indicator.current = 0;
}
XSync(app->dpy, False);
}
void paintDialog(AppInfo *app)
{
DialogInfo *d = app->dialog;
Drawable draw = d->dialogWindow;
int i;
XSetForeground(app->dpy, app->fillGC, d->w3.w.background);
XFillRectangle(app->dpy, draw, app->fillGC, 0, 0,
d->w3.w.width, d->w3.w.height);
if (d->w3.shadowThickness > 0) {
draw_shaded_rectangle(app->dpy, draw, 0, 0,
d->w3.w.width, d->w3.w.height,
d->w3.shadowThickness,
d->w3.topShadowColor,
d->w3.bottomShadowColor);
}
paintLabel(app, draw, d->label);
for (i = 0; i < d->indicator.count; i++) {
paintIndicator(app, draw, d->indicators[i]);
}
paintButton(app, draw, d->okButton);
paintButton(app, draw, d->cancelButton);
XSync(app->dpy, False);
}
void performGrab(AppInfo *app, int grabType, char *grabTypeName,
Bool shouldGrab, Bool *isGrabbed) {
if ((!(shouldGrab)) || (*isGrabbed)) {
return;
} else if ((GRAB_KEYBOARD != grabType) && (GRAB_POINTER != grabType)) {
fprintf(stderr, "%s[%ld]: performGrab: invalid grab type (%d).\n",
app->appName, (long) app->pid, grabType);
return;
} else {
int status = GrabInvalidTime; /* keep gcc -Wall from complaining */
unsigned int seconds = 0;
int helpful_message = 0;
/* keyboard and pointer */
Window grabWindow = app->dialog->dialogWindow;
Bool ownerEvents = False;
Bool pointerMode = GrabModeAsync;
Bool keyboardMode = GrabModeAsync;
/* pointer only */
unsigned int eventMask = ButtonPressMask | ButtonReleaseMask;
Window confineTo = None;
Cursor cursor = None;
*isGrabbed = True;
if (NULL == grabTypeName) {
fprintf(stderr, "%s[%ld]: performGrab: null grab type name.\n",
app->appName, (long) app->pid);
}
if (0 == app->grabFailTimeout) {
/* Ensure we try to perform the grab at least once. */
app->grabFailTimeout = 1;
}
while (seconds < app->grabFailTimeout) {
XSync(app->dpy, False);
switch (grabType) {
case GRAB_KEYBOARD:
status = XGrabKeyboard(app->dpy, grabWindow, ownerEvents,
pointerMode, keyboardMode, CurrentTime);
break;
case GRAB_POINTER:
status = XGrabPointer(app->dpy, grabWindow, ownerEvents,
eventMask, pointerMode, keyboardMode,
confineTo, cursor, CurrentTime);
break;
}
XSync(app->dpy, False);
if (GrabSuccess == status) {
if (helpful_message) {
fprintf(stderr, "%s[%ld]: Got %s.\n",
app->appName, (long) app->pid, grabTypeName);
}
break;
}
if (!helpful_message) {
fprintf(stderr, "%s[%ld]: Trying to grab %s ...\n",
app->appName, (long) app->pid, grabTypeName);
helpful_message = 1;
}
seconds += app->grabRetryInterval;
sleep(app->grabRetryInterval);
}
if (GrabSuccess != status) {
char *reason = "reason unknown";
switch (status) {
case AlreadyGrabbed:
reason = "someone else already has it";
break;
case GrabFrozen:
reason = "someone else has frozen it";
break;
case GrabInvalidTime:
reason = "bad grab time [this shouldn't happen]";
break;
case GrabNotViewable:
reason = "grab not viewable [this shouldn't happen]";
break;
}
fprintf(stderr, "%s[%ld]: Could not grab %s (%s)\n",
app->appName, (long) app->pid, grabTypeName, reason);
exitApp(app, EXIT_STATUS_ERROR);
}
}
}
void grabKeyboard(AppInfo *app)
{
performGrab(app, GRAB_KEYBOARD, "keyboard", app->grabKeyboard,
&(app->isKeyboardGrabbed));
}
void ungrabKeyboard(AppInfo *app)
{
if (app->grabKeyboard) {
XUngrabKeyboard(app->dpy, CurrentTime);
}
}
void grabPointer(AppInfo *app)
{
performGrab(app, GRAB_POINTER, "pointer", app->grabPointer,
&(app->isPointerGrabbed));
}
void ungrabPointer(AppInfo *app)
{
if (app->grabPointer) {
XUngrabPointer(app->dpy, CurrentTime);
}
}
void grabServer(AppInfo *app)
{
if ((!(app->grabServer)) || (app->isServerGrabbed)) {
return;
} else {
app->isServerGrabbed = True;
XSync(app->dpy, False);
XGrabServer(app->dpy);
XSync(app->dpy, False);
}
}
void ungrabServer(AppInfo *app)
{
if (app->grabServer) {
XUngrabServer(app->dpy);
}
}
void cleanUp(AppInfo *app)
{
cancelInputTimeout(app);
XDestroyWindow(app->dpy, app->dialog->dialogWindow);
destroyGCs(app);
destroyDialog(app);
if (app->buf) {
memset(app->buf, 0, app->bufSize);
}
freeIf(app->buf);
ungrabPointer(app);
ungrabKeyboard(app);
ungrabServer(app);
}
void exitApp(AppInfo *app, int exitCode)
{
cleanUp(app);
exit(exitCode);
}
void acceptAction(AppInfo *app)
{
int status = append_to_buf(&(app->buf), &(app->bufSize),
&(app->bufIndex), '\0');
if (APPEND_FAILURE == status) {
cleanUp(app);
outOfMemory(app, __LINE__);
}
fputs(app->buf, stdout);
fputc('\n', stdout);
exitApp(app, EXIT_STATUS_ACCEPT);
}
void cancelAction(AppInfo *app)
{
exitApp(app, EXIT_STATUS_CANCEL);
}
void backspacePassphrase(AppInfo *app)
{
if (0 >= app->bufIndex) {
XBell(app->dpy, 0);
return;
}
(app->bufIndex)--;
updateIndicators(app, -1);
}
void erasePassphrase(AppInfo *app)
{
if (0 >= app->bufIndex) {
XBell(app->dpy, 0);
return;
}
updateIndicators(app, 0);
app->bufIndex = 0;
}
void addToPassphrase(AppInfo *app, char c)
{
int status = append_to_buf(&(app->buf), &(app->bufSize),
&(app->bufIndex), c);
if (APPEND_FAILURE == status) {
cleanUp(app);
outOfMemory(app, __LINE__);
}
updateIndicators(app, 1);
}
void handleKeyPress(AppInfo *app, XEvent *event)
{
char s[2];
int n;
if (event->xkey.send_event) {
/* Pay no attention to synthetic key events. */
return;
}
cancelInputTimeout(app);
n = XLookupString(&(event->xkey), s, 1, NULL, NULL);
if (1 != n) {
return;
}
s[1] = '\0';
switch (s[0]) {
case '\010':
case '\177':
backspacePassphrase(app);
break;
case '\025':
case '\030':
erasePassphrase(app);
break;
case '\012':
case '\015':
acceptAction(app);
break;
case '\033':
cancelAction(app);
break;
default:
addToPassphrase(app, s[0]);
break;
}
}
Bool eventIsInsideButton(AppInfo *app, XEvent *event, ButtonInfo button)
{
/* 'gcc -Wall' complains about 'app' being an unused parameter.
* Tough. We might want to use it later, and then we don't have
* to change it in each place it's called. Performance won't suffer.
*/
int status = False;
int x, y;
switch(event->type) {
case ButtonPress:
case ButtonRelease:
x = event->xbutton.x;
y = event->xbutton.y;
break;
case MotionNotify:
x = event->xmotion.x;
y = event->xmotion.y;
break;
default:
return(False);
}
if ((x >= (button.w3.w.x + button.w3.borderWidth)) &&
(x < (button.w3.w.x + button.w3.w.width -
(2 * button.w3.borderWidth))) &&
(y >= (button.w3.w.y + button.w3.borderWidth)) &&
(y < (button.w3.w.y + button.w3.w.height -
(2 * button.w3.borderWidth)))) {
status = True;
}
return(status);
}
void handleButtonPress(AppInfo *app, XEvent *event)
{
DialogInfo *d = app->dialog;
cancelInputTimeout(app);
if (event->xbutton.button != Button1) {
return;
}
if (ButtonPress == event->type) {
if (eventIsInsideButton(app, event, d->okButton)) {
d->pressedButton = OK_BUTTON;
d->okButton.pressed = True;
paintButton(app, d->dialogWindow, d->okButton);
} else if (eventIsInsideButton(app, event, d->cancelButton)) {
d->pressedButton = CANCEL_BUTTON;
d->cancelButton.pressed = True;
paintButton(app, d->dialogWindow, d->cancelButton);
} else {
d->pressedButton = NO_BUTTON;
}
} else if (ButtonRelease == event->type) {
if (OK_BUTTON == d->pressedButton) {
if (eventIsInsideButton(app, event, d->okButton)) {
acceptAction(app);
} else {
if (d->okButton.pressed) {
d->okButton.pressed = False;
paintButton(app, d->dialogWindow, d->okButton);
}
}
} else if (CANCEL_BUTTON == d->pressedButton) {
if (eventIsInsideButton(app, event, d->cancelButton)) {
cancelAction(app);
} else {
if (d->cancelButton.pressed) {
d->cancelButton.pressed = False;
paintButton(app, d->dialogWindow, d->cancelButton);
}
}
}
d->pressedButton = NO_BUTTON;
}
}
void handlePointerMotion(AppInfo *app, XEvent *event)
{
DialogInfo *d = app->dialog;
if (NO_BUTTON == d->pressedButton) {
return;
} else if (OK_BUTTON == d->pressedButton) {
if (eventIsInsideButton(app, event, d->okButton)) {
if (!(d->okButton.pressed)) {
d->okButton.pressed = True;
paintButton(app, d->dialogWindow, d->okButton);
}
} else {
if (d->okButton.pressed) {
d->okButton.pressed = False;
paintButton(app, d->dialogWindow, d->okButton);
}
}
} else if (CANCEL_BUTTON == d->pressedButton) {
if (eventIsInsideButton(app, event, d->cancelButton)) {
if (!(d->cancelButton.pressed)) {
d->cancelButton.pressed = True;
paintButton(app, d->dialogWindow, d->cancelButton);
}
} else {
if (d->cancelButton.pressed) {
d->cancelButton.pressed = False;
paintButton(app, d->dialogWindow, d->cancelButton);
}
}
}
}
void handleInputTimeout(XtPointer data, XtIntervalId *timerId)
{
/* 'gcc -Wall' complains about 'timerId' being an unused parameter.
* Tough. Xt forces us to have it here. Like it.
*/
AppInfo *app = (AppInfo *) data;
if (app->inputTimeoutActive) {
app->inputTimeoutActive = False;
fprintf(stderr, "%s[%ld]: *Yawn*...timed out after %lu seconds.\n",
app->appName, (long) app->pid, (app->inputTimeout / 1000));
exitApp(app, EXIT_STATUS_TIMEOUT);
}
}
void cancelInputTimeout(AppInfo *app)
{
if (app->inputTimeoutActive) {
app->inputTimeoutActive = False;
XtRemoveTimeOut(app->inputTimeoutTimerId);
}
}
int main(int argc, char **argv)
{
AppInfo app;
XEvent event;
XineramaScreenInfo *screens;
int nscreens;
memset(&app, 0, sizeof(app));
progclass = "SshAskpass";
app.toplevelShell = XtAppInitialize(&(app.appContext), progclass,
NULL, 0, &argc, argv,
defaults, NULL, 0);
app.argc = argc;
app.argv = argv;
app.dpy = XtDisplay(app.toplevelShell);
app.screen = DefaultScreenOfDisplay(app.dpy);
app.rootWindow = RootWindowOfScreen(app.screen);
app.black = BlackPixel(app.dpy, DefaultScreen(app.dpy));
app.white = WhitePixel(app.dpy, DefaultScreen(app.dpy));
app.colormap = DefaultColormapOfScreen(app.screen);
app.resourceDb = XtDatabase(app.dpy);
XtGetApplicationNameAndClass(app.dpy, &progname, &progclass);
app.appName = progname;
app.appClass = progclass;
/* For resources.c. */
db = app.resourceDb;
/* Seconds after which keyboard/pointer grab fail. */
app.grabFailTimeout = 5;
/* Number of seconds to wait between grab attempts. */
app.grabRetryInterval = 1;
app.pid = getpid();
{
struct rlimit resourceLimit;
int status;
status = getrlimit(RLIMIT_CORE, &resourceLimit);
if (-1 == status) {
fprintf(stderr, "%s[%ld]: getrlimit failed (%s)\n", app.appName,
(long) app.pid, strerror(errno));
exit(EXIT_STATUS_ERROR);
}
resourceLimit.rlim_cur = 0;
status = setrlimit(RLIMIT_CORE, &resourceLimit);
if (-1 == status) {
fprintf(stderr, "%s[%ld]: setrlimit failed (%s)\n", app.appName,
(long) app.pid, strerror(errno));
exit(EXIT_STATUS_ERROR);
}
}
app.screen_width = WidthOfScreen(app.screen);
app.screen_height = HeightOfScreen(app.screen);
app.screen_xoffset = 0;
app.screen_yoffset = 0;
if (XineramaIsActive(app.dpy) &&
(screens = XineramaQueryScreens(app.dpy, &nscreens)) != NULL &&
nscreens) {
app.screen_width = screens[0].width;
app.screen_height = screens[0].height;
app.screen_xoffset = screens[0].x_org;
app.screen_yoffset = screens[0].y_org;
XFree(screens);
}
app.xResolution =
app.screen_width * 1000 / WidthMMOfScreen(app.screen);
app.yResolution =
app.screen_height * 1000 / HeightMMOfScreen(app.screen);
createDialog(&app);
createGCs(&app);
app.eventMask = 0;
app.eventMask |= ExposureMask;
app.eventMask |= ButtonPressMask;
app.eventMask |= ButtonReleaseMask;
app.eventMask |= Button1MotionMask;
app.eventMask |= KeyPressMask;
createDialogWindow(&app);
XMapWindow(app.dpy, app.dialog->dialogWindow);
if (app.inputTimeout > 0) {
app.inputTimeoutActive = True;
app.inputTimeoutTimerId =
XtAppAddTimeOut(app.appContext, app.inputTimeout,
handleInputTimeout, (XtPointer) &app);
}
while(True) {
XtAppNextEvent(app.appContext, &event);
switch (event.type) {
case Expose:
grabServer(&app);
grabKeyboard(&app);
grabPointer(&app);
if (event.xexpose.count) {
break;
}
paintDialog(&app);
break;
case ButtonPress:
case ButtonRelease:
handleButtonPress(&app, &event);
break;
case MotionNotify:
handlePointerMotion(&app, &event);
case KeyPress:
handleKeyPress(&app, &event);
break;
case ClientMessage:
if ((32 == event.xclient.format) &&
((unsigned long) event.xclient.data.l[0] ==
app.wmDeleteWindowAtom)) {
cancelAction(&app);
}
break;
default:
break;
}
}
fprintf(stderr, "%s[%ld]: This should not happen.\n", app.appName,
(long) app.pid);
return(EXIT_STATUS_ANOMALY);
}