e5b23895c8
ssh-askpass(1) is computing the size of its indicators relatively to the screen resolution. When multiple monitors are present, the support for Xinerama computes the dimensions of the window to be created relatively to the screen on which it will appear. But the computation of the indicator size is based on the size of the whole display, resulting in too small indicators (and too many of them if the screens hare layed out horizontally). Fix that by computing the resolution of the whole display before taking xinerama into account. ok djm@
1585 lines
47 KiB
C
1585 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;
|
|
}
|
|
}
|
|
free(s);
|
|
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;
|
|
|
|
app.xResolution =
|
|
app.screen_width * 1000 / WidthMMOfScreen(app.screen);
|
|
app.yResolution =
|
|
app.screen_height * 1000 / HeightMMOfScreen(app.screen);
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|