Beispiel #1
0
static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry,
                                              ObMenuFrame *frame)
{
    ObMenuEntryFrame *self;
    XSetWindowAttributes attr;

    self = g_slice_new0(ObMenuEntryFrame);
    self->entry = entry;
    self->frame = frame;

    menu_entry_ref(entry);

    attr.event_mask = ENTRY_EVENTMASK;
    self->window = createWindow(self->frame->window, CWEventMask, &attr);
    self->text = createWindow(self->window, 0, NULL);
    g_hash_table_insert(menu_frame_map, &self->window, self);
    g_hash_table_insert(menu_frame_map, &self->text, self);
    if ((entry->type == OB_MENU_ENTRY_TYPE_NORMAL) ||
        (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) {
        self->icon = createWindow(self->window, 0, NULL);
        g_hash_table_insert(menu_frame_map, &self->icon, self);
    }
    if (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
        self->bullet = createWindow(self->window, 0, NULL);
        g_hash_table_insert(menu_frame_map, &self->bullet, self);
    }

    XMapWindow(obt_display, self->window);
    XMapWindow(obt_display, self->text);

    window_add(&self->window, MENUFRAME_AS_WINDOW(self->frame));

    return self;
}
Beispiel #2
0
ObMenuFrame* menu_frame_new(ObMenu *menu, guint show_from, ObClient *client)
{
    ObMenuFrame *self;
    XSetWindowAttributes attr;

    self = g_slice_new0(ObMenuFrame);
    self->obwin.type = OB_WINDOW_CLASS_MENUFRAME;
    self->menu = menu;
    self->selected = NULL;
    self->client = client;
    self->direction_right = TRUE;
    self->show_from = show_from;

    attr.event_mask = FRAME_EVENTMASK;
    self->window = createWindow(obt_root(ob_screen),
                                CWEventMask, &attr);

    /* make it a popup menu type window */
    OBT_PROP_SET32(self->window, NET_WM_WINDOW_TYPE, ATOM,
                   OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_POPUP_MENU));

    XSetWindowBorderWidth(obt_display, self->window, ob_rr_theme->mbwidth);
    XSetWindowBorder(obt_display, self->window,
                     RrColorPixel(ob_rr_theme->menu_border_color));

    self->a_items = RrAppearanceCopy(ob_rr_theme->a_menu);

    window_add(&self->window, MENUFRAME_AS_WINDOW(self));
    stacking_add(MENUFRAME_AS_WINDOW(self));

    return self;
}
Beispiel #3
0
ObPopup *popup_new(void)
{
    XSetWindowAttributes attrib;
    ObPopup *self = g_slice_new0(ObPopup);

    self->obwin.type = OB_WINDOW_CLASS_INTERNAL;
    self->gravity = NorthWestGravity;
    self->x = self->y = self->textw = self->h = 0;
    self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_bg);
    self->a_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label);
    self->iconwm = self->iconhm = 1;

    attrib.override_redirect = True;
    self->bg = XCreateWindow(obt_display, obt_root(ob_screen),
                             0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
                             InputOutput, RrVisual(ob_rr_inst),
                             CWOverrideRedirect, &attrib);

    self->text = XCreateWindow(obt_display, self->bg,
                               0, 0, 1, 1, 0, RrDepth(ob_rr_inst),
                               InputOutput, RrVisual(ob_rr_inst), 0, NULL);

    XSetWindowBorderWidth(obt_display, self->bg, ob_rr_theme->obwidth);
    XSetWindowBorder(obt_display, self->bg,
                     RrColorPixel(ob_rr_theme->osd_border_color));

    XMapWindow(obt_display, self->text);

    stacking_add(INTERNAL_AS_WINDOW(self));
    window_add(&self->bg, INTERNAL_AS_WINDOW(self));
    return self;
}
Beispiel #4
0
void frame_grab_client(struct wm_frame *self)
{
	XReparentWindow(t_display, self->client->window, self->window, 0, 0);
	if(wm_state() == WM_STATE_STARTING)
		++self->client->ignore_unmaps;
	XSelectInput(t_display, self->window, FRAME_EVENTMASK);
	window_add(&self->window, CLIENT_AS_WINDOW(self->client));
}
Beispiel #5
0
int main(int argc, char **argv)
{
	int cnt, x, y, i = 0, verbose = 0;
	Window win = 0;
	Bool keysymMappingInitialized = False;
	int rc = 0;
	int inputEvents[100];
	int inputEventsIndex = 0;
	int iEvent = 0;

	if (argc == 1)
		usage(argv[0]);

	const char* log_file = NULL;
	if (streq(argv[1],"-o") || streq(argv[1],"--logfile")) {
		i++;

		if (++i > argc)
			usage(argv[0]);

		log_file = argv[i];
	}
	report_init(log_file);

	if (!xhandler_init(getenv("DISPLAY")))
		exit(1);

	report_add_message(xhandler_get_server_time(), "Startup\n");

	/* initialize subsystems */
	xemu_init(xhandler.display);
	scheduler_init(xhandler.display);
	window_init(xhandler.display);
	application_init();

	/*
	 * Process the command line options.
	 * Skip emulation options (--click, --drag, --key, --type), but remember they index
	 * and process them later.
	 */
	while (++i < argc) {

		if (streq(argv[i],"-v") || streq(argv[i],"--verbose")) {
			verbose = 1;
			continue;
		}

		if (streq(argv[i], "-id") || streq(argv[i], "--id")) {
			char name[PATH_MAX];
			if (++i >= argc)
				usage(argv[0]);

			cnt = sscanf(argv[i], "0x%lx", &win);
			if (cnt < 1) {
				cnt = sscanf(argv[i], "%lu", &win);
			}
			if (cnt < 1) {
				fprintf(stderr, "*** invalid window id '%s'\n", argv[i]);
				usage(argv[0]);
			}
			sprintf(name, "0x%lx", win);
			if (!window_add(win, application_monitor(name))) {
				fprintf(stderr, "Could not setup damage monitoring for window 0x%lx!\n", win);
				exit(1);
			}
			if (verbose)
				report_add_message(REPORT_LAST_TIMESTAMP, "Monitoring window 0x%lx\n", win);

			continue;
		}

		if (streq(argv[i], "-a") || streq(argv[i], "--application")) {
			if (++i >= argc)
				usage(argv[0]);

			response.application = application_monitor(argv[i]);
			if (response.application && verbose) {
				report_add_message(REPORT_LAST_TIMESTAMP, "Monitoring application '%s'\n", argv[i]);
			}
			if (!strcmp(argv[i], "*")) {
				application_set_monitor_all(true);
			}
			continue;
		}

		if (streq("-c", argv[i]) || streq("--click", argv[i])) {
			if (!xemu.pointer.dev) {
				fprintf(stderr, "Failed to open pointer device, unable to simulate pointer events.\n");
				exit(-1);
			}
			if (inputEventsIndex == ASIZE(inputEvents)) {
				fprintf(stderr, "Too many input events specified\n");
				exit(-1);
			}
			if (!argv[i + 1] || !match_regex(argv[i + 1], "^[0-9]+x[0-9]+(,[0-9]+)?$")) {
				fprintf(stderr, "Failed to parse --c options: %s\n", argv[i + 1]);
				exit(-1);
			}
			inputEvents[inputEventsIndex++] = i;
			if (++i >= argc)
				usage(argv[0]);

			continue;
		}

		if (streq("-l", argv[i]) || streq("--level", argv[i])) {
			if (++i >= argc)
				usage(argv[0]);

			if (!strcmp(argv[i], "raw")) {
				window_set_damage_level(XDamageReportRawRectangles);
			} else if (!strcmp(argv[i], "delta")) {
				window_set_damage_level(XDamageReportDeltaRectangles);
			} else if (!strcmp(argv[i], "box")) {
				window_set_damage_level(XDamageReportDeltaRectangles);
			} else if (!strcmp(argv[i], "nonempty")) {
				window_set_damage_level(XDamageReportNonEmpty);
			} else {
				fprintf(stderr, "Unrecongnized damage level: %s\n", argv[i]);
				usage(argv[0]);
			}
			if (verbose)
				report_add_message(REPORT_LAST_TIMESTAMP, "Setting damage report level to %s\n", argv[i]);
			continue;
		}

		if (streq("-x", argv[i]) || streq("--exclude", argv[i])) {
			char* exclude[] = { "none", "less", "greater" };

			if (options.exclude_rules != EXCLUDE_NONE) {
				fprintf(stderr, "Duplicated --exclude parameter detected. Aborting\n");
				exit(-1);
			}

			if (++i >= argc)
				usage(argv[0]);
			char rules[32] = "";
			if ((cnt = sscanf(argv[i], "%ux%u,%s", &options.exclude_rect.width, &options.exclude_rect.height, rules)) >= 2) {
				options.exclude_size = 0;
			} else if ((cnt = sscanf(argv[i], "%u,%s", &options.exclude_size, rules)) >= 1) {
				options.exclude_rect.width = 0;
				options.exclude_rect.height = 0;
			} else {
				fprintf(stderr, "*** failed to parse '%s'\n", argv[i]);
				usage(argv[0]);
			}
			options.exclude_rules = *rules && !strcmp(rules, "greater") ? EXCLUDE_GREATER : EXCLUDE_LESS;
			if (verbose) {
				if (options.exclude_size) {
					report_add_message(REPORT_LAST_TIMESTAMP, "Excluding damage areas %s than %d pixels\n", exclude[options.exclude_rules],
							options.exclude_size);
				} else {
					report_add_message(REPORT_LAST_TIMESTAMP, "Excluding damage areas %s than (%dx%d)\n", exclude[options.exclude_rules],
							options.exclude_rect.width, options.exclude_rect.height);
				}
			}
			continue;
		}

		if (streq("-m", argv[i]) || streq("--monitor", argv[i])) {
			if (options.interested_damage_rect.width || options.interested_damage_rect.height || options.interested_damage_rect.x
					|| options.interested_damage_rect.y) {
				fprintf(stderr, "Duplicated --monitor parameter detected. Aborting\n");
				exit(-1);
			}
			if (++i >= argc)
				usage(argv[0]);

			if ((cnt = sscanf(argv[i], "%ux%u+%u+%u", &options.interested_damage_rect.width, &options.interested_damage_rect.height,
					&options.interested_damage_rect.x, &options.interested_damage_rect.y)) != 4) {
				fprintf(stderr, "*** failed to parse '%s'\n", argv[i]);
				usage(argv[0]);
			}
			if (verbose) {
				report_add_message(REPORT_LAST_TIMESTAMP, "Set monitor rect to %ix%i+%i+%i\n", options.interested_damage_rect.width,
						options.interested_damage_rect.height, options.interested_damage_rect.x, options.interested_damage_rect.y);
			}
			continue;
		}

		if (streq("-w", argv[i]) || streq("--wait", argv[i])) {
			if (++i >= argc)
				usage(argv[0]);

			if (options.damage_wait_secs >= 0) {
				fprintf(stderr, "Duplicate -w(--wait) option detected. Discarding the previous value\n");
			}
			if ((options.damage_wait_secs = atoi(argv[i])) < 0) {
				fprintf(stderr, "*** failed to parse '%s'\n", argv[i]);
				usage(argv[0]);
			}
			if (verbose)
				report_add_message(REPORT_LAST_TIMESTAMP, "Set event timeout to %isecs\n", options.damage_wait_secs);

			continue;
		}

		if (streq("-b", argv[i]) || streq("--break", argv[i])) {
			if (options.break_timeout || options.break_on_damage) {
				fprintf(stderr, "Duplicate -b(--break)option detected. Discarding the previous value\n");
				options.break_timeout = 0;
				options.break_on_damage = 0;
			}
			if (++i >= argc)
				usage(argv[0]);

			if (!strncmp(argv[i], "damage", 6)) {
				sscanf(argv[i] + 6, ",%d", &options.break_on_damage);
				if (!options.break_on_damage)
					options.break_on_damage = 1;
				if (verbose)
					report_add_message(REPORT_LAST_TIMESTAMP, "Break wait on the %d damage event\n", options.break_on_damage);
			} else {
				if ((options.break_timeout = atoi(argv[i])) < 0) {
					fprintf(stderr, "*** failed to parse '%s'\n", argv[i]);
					usage(argv[0]);
				}
				if (verbose)
					report_add_message(REPORT_LAST_TIMESTAMP, "Set break timout to %imsecs\n", options.break_timeout);
			}
			continue;
		}

		if (streq("-d", argv[i]) || streq("--drag", argv[i])) {
			if (!xemu.pointer.dev) {
				fprintf(stderr, "Failed to open pointer device, unable to simulate pointer events.\n");
				exit(-1);
			}
			if (inputEventsIndex == ASIZE(inputEvents)) {
				fprintf(stderr, "Too many input events specified\n");
				exit(-1);
			}
			if (!argv[i + 1] || (!match_regex(argv[i + 1], "^([0-9]+,)?(([0-9]+x[0-9]+,([0-9]+,)?)+[0-9]+x[0-9]+)$") &&
				 (!match_regex(argv[i + 1], "[0-9]+x[0-9]+-[0-9]+x[0-9]+") ||
				  !match_regex(argv[i + 1], "^(((([0-9]+,)?([0-9]+x[0-9]+)|([0-9]+x[0-9]+-[0-9]+x[0-9]+(\\*[0-9]+)?(\\+[1-9][0-9]*)?)),?)+)$") ) ) ) {
				fprintf(stderr, "Failed to parse --drag options: %s\n", argv[i + 1]);
				exit(-1);
			}
			inputEvents[inputEventsIndex++] = i;

			if (++i >= argc)
				usage(argv[0]);
			continue;
		}

		if (streq("-k", argv[i]) || streq("--key", argv[i])) {
			if (!xemu.keyboard.dev) {
				fprintf(stderr, "Failed to open keyboard device, unable to simulate keyboard events.\n");
				exit(-1);
			}
			if (inputEventsIndex == ASIZE(inputEvents)) {
				fprintf(stderr, "Too many input events specified\n");
				exit(-1);
			}
			inputEvents[inputEventsIndex++] = i;
			if (++i >= argc)
				usage(argv[0]);

			continue;
		}

		if (streq("-t", argv[i]) || streq("--type", argv[i])) {
			if (!xemu.keyboard.dev) {
				fprintf(stderr, "Failed to open keyboard device, unable to simulate keyboard events.\n");
				exit(-1);
			}
			if (inputEventsIndex == ASIZE(inputEvents)) {
				fprintf(stderr, "Too many input events specified\n");
				exit(-1);
			}
			inputEvents[inputEventsIndex++] = i;
			if (++i >= argc)
				usage(argv[0]);

			if (!keysymMappingInitialized) {
				xemu_load_keycodes();
				keysymMappingInitialized = True;
			}

			continue;
		}

		/* since moving from command sequence approach the inspect parameter is deprecated */
		if (streq("-i", argv[i]) || streq("--inspect", argv[i])) {
			if (verbose)
				report_add_message(REPORT_LAST_TIMESTAMP, "Just displaying damage events until timeout\n");
			continue;
		}

		/* */
		if (streq("-u", argv[i]) || streq("--user", argv[i]) ||
				(xrecord.motion = (streq("-U", argv[i]) || streq("--user-all", argv[i])) ) ) {
			xinput_init(xhandler.display);
			if (verbose)
				report_add_message(REPORT_LAST_TIMESTAMP, "Reporting user input events\n");

			continue;
		}

		if (streq(argv[i], "-r") || streq(argv[i], "--response")) {
			if (++i >= argc)
				usage(argv[0]);
			char option[500];
			cnt = sscanf(argv[i], "%u,%s", &response.timeout, option);
			if (cnt < 1) {
				fprintf(stderr, "*** invalid response timeout value '%s'\n", argv[i]);
				usage(argv[0]);
			}
			if (cnt < 2) {
				report_set_silent(true);
			} else {
				if (strcmp(option, "verbose")) {
					fprintf(stderr, "*** invalid response option '%s'\n", argv[i]);
					usage(argv[0]);
				}
			}
			application_monitor_screen();
			xinput_init(xhandler.display);
			if (verbose)
				report_add_message(REPORT_LAST_TIMESTAMP, "Monitoring application response time\n");

			continue;
		}

		fprintf(stderr, "*** Dont understand  %s\n", argv[i]);
		usage(argv[0]);
	}

	/* start monitoring the root window if no targets are specified */
	if ((window_empty() && application_empty()) || response.timeout) {
		application_monitor(ROOT_WINDOW_RESOURCE);
	}

	window_monitor_all();
	application_start_monitor();

	/* eat first damage event when options.break_on_damage set */
	if (options.break_on_damage)
		xhandler_eat_damage();

	/* monitor the whole screen of no area is specified */
	if (!options.interested_damage_rect.width && !options.interested_damage_rect.height && !options.interested_damage_rect.x
			&& !options.interested_damage_rect.y) {
		options.interested_damage_rect.x = 0;
		options.interested_damage_rect.y = 0;
		options.interested_damage_rect.width = DisplayWidth(xhandler.display, DefaultScreen(xhandler.display));
		options.interested_damage_rect.height = DisplayHeight(xhandler.display, DefaultScreen(xhandler.display));
	}

	/* emulate user input */

	for (iEvent = 0; iEvent < inputEventsIndex; iEvent++) {
		i = inputEvents[iEvent];

		if (!strcmp("-c", argv[i]) || !strcmp("--click", argv[i])) {
			unsigned long delay = 0;
			Time start = 0;
			cnt = sscanf(argv[++i], "%ux%u,%lu", &x, &y, &delay);
			if (cnt == 2) {
				start = xhandler_get_server_time();
				report_add_message(start, "Using no delay between press/release\n");
				delay = 0;
			} else if (cnt != 3) {
				fprintf(stderr, "cnt: %d\n", cnt);
				fprintf(stderr, "*** failed to parse '%s'\n", argv[i]);
				usage(argv[0]);
			}
			/* Send the event */
			start = xemu_button_event(x, y, delay);
			report_add_message(start, "Clicked %ix%i\n", x, y);

			continue;
		}

		if (!strcmp("-d", argv[i]) || !strcmp("--drag", argv[i])) {
			Time drag_time;
			char *s = NULL, *p = NULL;
			int button_state = XR_BUTTON_STATE_PRESS;

			s = p = argv[++i];
			int delay = DEFAULT_DRAG_DELAY;
			int x1, y1, x2, y2;
			while (p) {
				p = strchr(s, ',');
				if (p) {
					*p++ = '\0';
				}
				int count = DEFAULT_DRAG_COUNT;
				cnt = sscanf(s, "%ix%i-%ix%i*%i+%i", &x1, &y1, &x2, &y2, &delay, &count);
				if (cnt >= 4) {
					drag_time = xemu_drag_event(x1, y1, button_state, delay);
					button_state = XR_BUTTON_STATE_NONE;
					report_add_message(drag_time, "Dragged to %ix%i\n", x1, y1);

					int xdev = (x2 - x1) / (count + 1);
					int ydev = (y2 - y1) / (count + 1);
					for (i = 1; i <= count; i++) {
						x = x1 + xdev * i;
						y = y1 + ydev * i;
						drag_time = xemu_drag_event(x, y, button_state, delay);
						report_add_message(drag_time, "Dragged to %ix%i\n", x, y);
					}
					if (!p) button_state = XR_BUTTON_STATE_RELEASE;
					drag_time = xemu_drag_event(x2, y2, button_state, delay);
					report_add_message(drag_time, "Dragged to %ix%i\n", x2, y2);
				}
				else if (cnt == 2) {
					/* Send the event */
					if (!p) {
						if (button_state == XR_BUTTON_STATE_PRESS) {
							fprintf(stderr, "*** Need at least 2 drag points!\n");
							usage(argv[0]);
						}
						button_state = XR_BUTTON_STATE_RELEASE;
					}
					drag_time = xemu_drag_event(x1, y1, button_state, delay);
					report_add_message(drag_time, "Dragged to %ix%i\n", x1, y1);

					/* Make sure button state set to none after first point */
					button_state = XR_BUTTON_STATE_NONE;

					/* reset the delay to default value */
					delay = DEFAULT_DRAG_DELAY;
				} else if (cnt == 1) {
					delay = x1;
				} else {
					fprintf(stderr, "*** failed to parse '%s'\n", argv[i]);
					usage(argv[0]);
				}
				s = p;
			}
			continue;
		}

		if (!strcmp("-k", argv[i]) || !strcmp("--key", argv[i])) {
			char *key = NULL;
			char separator;
			unsigned long delay = 0;
			Time start = 0;

			cnt = sscanf(argv[++i], "%a[^,]%c%lu", &key, &separator, &delay);
			if (cnt == 1) {
				report_add_message(REPORT_LAST_TIMESTAMP, "Using default delay between press/release\n", delay);
				delay = DEFAULT_KEY_DELAY;
			} else if (cnt != 3 || separator != ',') {
				fprintf(stderr, "cnt: %d\n", cnt);
				fprintf(stderr, "*** failed to parse '%s'\n", argv[i]);
				if (key != NULL)
					free(key);
				usage(argv[0]);
			}
			start = xemu_send_key(key, delay);
			report_add_message(start, "Simulating keypress/-release pair (keycode '%s')\n", key);
			free(key);

			continue;
		}

		if (!strcmp("-t", argv[i]) || !strcmp("--type", argv[i])) {
			Time start = xemu_send_string(argv[++i]);
			report_add_message(start, "Simulated keys for '%s'\n", argv[i]);

			continue;
		}

	}

	/* setting the default wait period */
	if (options.damage_wait_secs < 0) {
		options.damage_wait_secs = 5;
	}

	signal(SIGINT, abort_wait);
	/* wait for damage events */
	rc = wait_response();

	scheduler_fini();

	report_flush_queue();
	report_fini();
	xinput_fini();
	xemu_fini();

	window_fini();
	application_fini();

	xhandler_fini();


	return rc;
}
Beispiel #6
0
ObPrompt* prompt_new(const gchar *msg, const gchar *title,
                     const ObPromptAnswer *answers, gint n_answers,
                     gint default_result, gint cancel_result,
                     ObPromptCallback func, ObPromptCleanup cleanup,
                     gpointer data)
{
    ObPrompt *self;
    XSetWindowAttributes attrib;
    gint i;

    attrib.override_redirect = FALSE;

    self = g_slice_new0(ObPrompt);
    self->ref = 1;
    self->func = func;
    self->cleanup = cleanup;
    self->data = data;
    self->default_result = default_result;
    self->cancel_result = cancel_result;
    self->super.type = OB_WINDOW_CLASS_PROMPT;
    self->super.window = XCreateWindow(obt_display, obt_root(ob_screen),
                                       0, 0, 1, 1, 0,
                                       CopyFromParent, InputOutput,
                                       CopyFromParent,
                                       CWOverrideRedirect,
                                       &attrib);
    self->ic = obt_keyboard_context_new(self->super.window,
                                        self->super.window);

    /* make it a dialog type window */
    OBT_PROP_SET32(self->super.window, NET_WM_WINDOW_TYPE, ATOM,
                   OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG));

    /* set the window's title */
    if (title)
        OBT_PROP_SETS(self->super.window, NET_WM_NAME, utf8, title);

    /* listen for key presses on the window */
    self->event_mask = KeyPressMask;

    /* set up the text message widow */
    self->msg.text = g_strdup(msg);
    self->msg.window = XCreateWindow(obt_display, self->super.window,
                                     0, 0, 1, 1, 0,
                                     CopyFromParent, InputOutput,
                                     CopyFromParent, 0, NULL);
    XMapWindow(obt_display, self->msg.window);

    /* set up the buttons from the answers */

    self->n_buttons = n_answers;
    if (!self->n_buttons)
        self->n_buttons = 1;

    self->button = g_new0(ObPromptElement, self->n_buttons);

    if (n_answers == 0) {
        g_assert(self->n_buttons == 1); /* should be set to this above.. */
        self->button[0].text = g_strdup(_("OK"));
    }
    else {
        g_assert(self->n_buttons > 0);
        for (i = 0; i < self->n_buttons; ++i) {
            self->button[i].text = g_strdup(answers[i].text);
            self->button[i].result = answers[i].result;
        }
    }

    for (i = 0; i < self->n_buttons; ++i) {
        self->button[i].window = XCreateWindow(obt_display, self->super.window,
                                               0, 0, 1, 1, 0,
                                               CopyFromParent, InputOutput,
                                               CopyFromParent, 0, NULL);
        XMapWindow(obt_display, self->button[i].window);
        window_add(&self->button[i].window, PROMPT_AS_WINDOW(self));

        /* listen for button presses on the buttons */
        XSelectInput(obt_display, self->button[i].window,
                     ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
    }

    prompt_list = g_list_prepend(prompt_list, self);

    return self;
}