#define NCOLOURS (lenof(((Config *)0)->colours))
struct gui_data {
- GtkWidget *area;
+ GtkWidget *area, *sbar;
+ GtkBox *hbox;
+ GtkAdjustment *sbar_adjust;
GdkPixmap *pixmap;
GdkFont *fonts[2]; /* normal and bold (for now!) */
GdkCursor *rawcursor, *textcursor;
GdkColor cols[NCOLOURS];
GdkColormap *colmap;
- GdkGC *black_gc, *white_gc;
+ wchar_t *pastein_data;
+ int pastein_data_len;
+ char *pasteout_data;
+ int pasteout_data_len;
+ int font_width, font_height;
};
static struct gui_data the_inst;
if (inst->pixmap)
gdk_pixmap_unref(inst->pixmap);
- inst->pixmap = gdk_pixmap_new(widget->window, 9*80, 15*24, -1);
+ inst->pixmap = gdk_pixmap_new(widget->window,
+ (cfg.width * inst->font_width +
+ 2*cfg.window_border),
+ (cfg.height * inst->font_height +
+ 2*cfg.window_border), -1);
- inst->fonts[0] = gdk_font_load("9x15t"); /* XXCONFIG */
- inst->fonts[1] = NULL; /* XXCONFIG */
- inst->black_gc = widget->style->black_gc;
- inst->white_gc = widget->style->white_gc;
+ {
+ GdkGC *gc;
+ gc = gdk_gc_new(inst->area->window);
+ gdk_gc_set_foreground(gc, &inst->cols[18]); /* default background */
+ gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,
+ cfg.width * inst->font_width + 2*cfg.window_border,
+ cfg.height * inst->font_height + 2*cfg.window_border);
+ gdk_gc_unref(gc);
+ }
/*
* Set up the colour map.
* Pass the exposed rectangle to terminal.c, which will call us
* back to do the actual painting.
*/
- term_paint(NULL,
- event->area.x / 9, event->area.y / 15,
- (event->area.x + event->area.width - 1) / 9,
- (event->area.y + event->area.height - 1) / 15);
+ if (inst->pixmap) {
+ gdk_draw_pixmap(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+ inst->pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ }
return TRUE;
}
* - nethack mode
* - alt+numpad
* - Compose key (!!! requires Unicode faff before even trying)
- * - Shift-Ins for paste (need to deal with pasting first)
*/
+ /*
+ * Shift-PgUp and Shift-PgDn don't even generate keystrokes
+ * at all.
+ */
+ if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) {
+ term_scroll(0, -cfg.height/2);
+ return TRUE;
+ }
+ if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) {
+ term_scroll(0, +cfg.height/2);
+ return TRUE;
+ }
+
+ /*
+ * Neither does Shift-Ins.
+ */
+ if (event->keyval == GDK_Insert && (event->state & GDK_SHIFT_MASK)) {
+ request_paste();
+ return TRUE;
+ }
+
/* ALT+things gives leading Escape. */
output[0] = '\033';
strncpy(output+1, event->string, 31);
return TRUE;
}
+gint button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ struct gui_data *inst = (struct gui_data *)data;
+ int shift, ctrl, alt, x, y, button, act;
+
+ shift = event->state & GDK_SHIFT_MASK;
+ ctrl = event->state & GDK_CONTROL_MASK;
+ alt = event->state & GDK_MOD1_MASK;
+ if (event->button == 1)
+ button = MBT_LEFT;
+ else if (event->button == 2)
+ button = MBT_MIDDLE;
+ else if (event->button == 3)
+ button = MBT_RIGHT;
+ else
+ return FALSE; /* don't even know what button! */
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS: act = MA_CLICK; break;
+ case GDK_BUTTON_RELEASE: act = MA_RELEASE; break;
+ case GDK_2BUTTON_PRESS: act = MA_2CLK; break;
+ case GDK_3BUTTON_PRESS: act = MA_3CLK; break;
+ default: return FALSE; /* don't know this event type */
+ }
+
+ if (send_raw_mouse && !(cfg.mouse_override && shift) &&
+ act != MA_CLICK && act != MA_RELEASE)
+ return TRUE; /* we ignore these in raw mouse mode */
+
+ x = (event->x - cfg.window_border) / inst->font_width;
+ y = (event->y - cfg.window_border) / inst->font_height;
+
+ term_mouse(button, act, x, y, shift, ctrl, alt);
+
+ return TRUE;
+}
+
+gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
+{
+ struct gui_data *inst = (struct gui_data *)data;
+ int shift, ctrl, alt, x, y, button;
+
+ shift = event->state & GDK_SHIFT_MASK;
+ ctrl = event->state & GDK_CONTROL_MASK;
+ alt = event->state & GDK_MOD1_MASK;
+ if (event->state & GDK_BUTTON1_MASK)
+ button = MBT_LEFT;
+ else if (event->state & GDK_BUTTON2_MASK)
+ button = MBT_MIDDLE;
+ else if (event->state & GDK_BUTTON3_MASK)
+ button = MBT_RIGHT;
+ else
+ return FALSE; /* don't even know what button! */
+
+ x = (event->x - cfg.window_border) / inst->font_width;
+ y = (event->y - cfg.window_border) / inst->font_height;
+
+ term_mouse(button, MA_DRAG, x, y, shift, ctrl, alt);
+
+ return TRUE;
+}
+
gint timer_func(gpointer data)
{
/* struct gui_data *inst = (struct gui_data *)data; */
void write_clip(wchar_t * data, int len, int must_deselect)
{
- /* FIXME: currently ignored */
+ if (inst->pasteout_data)
+ sfree(inst->pasteout_data);
+ inst->pasteout_data = smalloc(len);
+ inst->pasteout_data_len = len;
+ wc_to_mb(0, 0, data, len, inst->pasteout_data, inst->pasteout_data_len,
+ NULL, NULL);
+
+ if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,
+ GDK_CURRENT_TIME)) {
+ gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING, 1);
+ }
+}
+
+void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
+ guint info, guint time_stamp, gpointer data)
+{
+ gtk_selection_data_set(seldata, GDK_SELECTION_TYPE_STRING, 8,
+ inst->pasteout_data, inst->pasteout_data_len);
+}
+
+gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
+ gpointer data)
+{
+ term_deselect();
+ if (inst->pasteout_data)
+ sfree(inst->pasteout_data);
+ inst->pasteout_data = NULL;
+ inst->pasteout_data_len = 0;
+ return TRUE;
+}
+
+void request_paste(void)
+{
+ /*
+ * In Unix, pasting is asynchronous: all we can do at the
+ * moment is to call gtk_selection_convert(), and when the data
+ * comes back _then_ we can call term_do_paste().
+ */
+ gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
+}
+
+void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
+ gpointer data)
+{
+ if (seldata->length <= 0 ||
+ seldata->type != GDK_SELECTION_TYPE_STRING)
+ return; /* Nothing happens. */
+
+ if (inst->pastein_data)
+ sfree(inst->pastein_data);
+
+ inst->pastein_data = smalloc(seldata->length * sizeof(wchar_t));
+ inst->pastein_data_len = seldata->length;
+ mb_to_wc(0, 0, seldata->data, seldata->length,
+ inst->pastein_data, inst->pastein_data_len);
+
+ term_do_paste();
}
void get_clip(wchar_t ** p, int *len)
{
if (p) {
- /* FIXME: currently nonfunctional */
- *p = NULL;
- *len = 0;
+ *p = inst->pastein_data;
+ *len = inst->pastein_data_len;
}
}
void set_sbar(int total, int start, int page)
{
- /* FIXME: currently ignored */
+ inst->sbar_adjust->lower = 0;
+ inst->sbar_adjust->upper = total;
+ inst->sbar_adjust->value = start;
+ inst->sbar_adjust->page_size = page;
+ inst->sbar_adjust->step_increment = 1;
+ inst->sbar_adjust->page_increment = page/2;
+ gtk_adjustment_changed(inst->sbar_adjust);
+}
+
+void scrollbar_moved(GtkAdjustment *adj, gpointer data)
+{
+ term_scroll(1, (int)adj->value);
}
void sys_cursor(int x, int y)
}
gdk_gc_set_foreground(gc, &inst->cols[nbg]);
- gdk_draw_rectangle(inst->pixmap, gc, 1, x*9, y*15, len*9, 15);
-
+ gdk_draw_rectangle(inst->pixmap, gc, 1,
+ x*inst->font_width+cfg.window_border,
+ y*inst->font_height+cfg.window_border,
+ len*inst->font_width, inst->font_height);
+
gdk_gc_set_foreground(gc, &inst->cols[nfg]);
gdk_draw_text(inst->pixmap, inst->fonts[0], gc,
- x*9, y*15 + inst->fonts[0]->ascent, text, len);
+ x*inst->font_width+cfg.window_border,
+ y*inst->font_height+cfg.window_border+inst->fonts[0]->ascent,
+ text, len);
if (attr & ATTR_UNDER) {
int uheight = inst->fonts[0]->ascent + 1;
- if (uheight >= 15)
- uheight = 14;
- gdk_draw_line(inst->pixmap, gc, x*9, y*15 + uheight,
- (x+len)*9-1, y*15+uheight);
+ if (uheight >= inst->font_height)
+ uheight = inst->font_height - 1;
+ gdk_draw_line(inst->pixmap, gc, x*inst->font_width+cfg.window_border,
+ y*inst->font_height + uheight + cfg.window_border,
+ (x+len)*inst->font_width-1+cfg.window_border,
+ y*inst->font_height + uheight + cfg.window_border);
}
gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
- x*9, y*15, x*9, y*15, len*9, 15);
+ x*inst->font_width+cfg.window_border,
+ y*inst->font_height+cfg.window_border,
+ x*inst->font_width+cfg.window_border,
+ y*inst->font_height+cfg.window_border,
+ len*inst->font_width, inst->font_height);
}
void do_cursor(Context ctx, int x, int y, char *text, int len,
do_text(ctx, x, y, text, len, attr, lattr);
if (passive) {
gdk_gc_set_foreground(gc, &inst->cols[NCOLOURS-1]);
- gdk_draw_rectangle(inst->pixmap, gc, 0, x*9, y*15, len*9-1, 15-1);
+ gdk_draw_rectangle(inst->pixmap, gc, 0,
+ x*inst->font_width+cfg.window_border,
+ y*inst->font_height+cfg.window_border,
+ len*inst->font_width-1, inst->font_height-1);
gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
- x*9, y*15, x*9, y*15, len*9, 15);
+ x*inst->font_width+cfg.window_border,
+ y*inst->font_height+cfg.window_border,
+ x*inst->font_width+cfg.window_border,
+ y*inst->font_height+cfg.window_border,
+ len*inst->font_width, inst->font_height);
}
}
do_defaults(NULL, &cfg);
+ inst->fonts[0] = gdk_font_load(cfg.font);
+ inst->fonts[1] = NULL; /* FIXME: what about bold font? */
+ inst->font_width = gdk_char_width(inst->fonts[0], ' ');
+ inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
+
init_ucs();
back = &pty_backend;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
inst->area = gtk_drawing_area_new();
gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area),
- 9*80, 15*24);/* FIXME: proper resizing stuff */
+ inst->font_width * cfg.width + 2*cfg.window_border,
+ inst->font_height * cfg.height + 2*cfg.window_border);
+ inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0));
+ inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust);
+ inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
+ gtk_box_pack_start(inst->hbox, inst->area, FALSE, FALSE, 0);
+ gtk_box_pack_start(inst->hbox, inst->sbar, FALSE, FALSE, 0);
- gtk_container_add(GTK_CONTAINER(window), inst->area);
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(inst->hbox));
+
+ {
+ GdkGeometry geom;
+ geom.min_width = inst->font_width + 2*cfg.window_border;
+ geom.min_height = inst->font_height + 2*cfg.window_border;
+ geom.max_width = geom.max_height = -1;
+ geom.base_width = 2*cfg.window_border;
+ geom.base_height = 2*cfg.window_border;
+ geom.width_inc = inst->font_width;
+ geom.height_inc = inst->font_height;
+ geom.min_aspect = geom.max_aspect = 0;
+ gtk_window_set_geometry_hints(GTK_WINDOW(window), inst->area, &geom,
+ GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE |
+ GDK_HINT_RESIZE_INC);
+ }
gtk_signal_connect(GTK_OBJECT(window), "destroy",
GTK_SIGNAL_FUNC(destroy), inst);
GTK_SIGNAL_FUNC(configure_area), inst);
gtk_signal_connect(GTK_OBJECT(inst->area), "expose_event",
GTK_SIGNAL_FUNC(expose_area), inst);
+ gtk_signal_connect(GTK_OBJECT(inst->area), "button_press_event",
+ GTK_SIGNAL_FUNC(button_event), inst);
+ gtk_signal_connect(GTK_OBJECT(inst->area), "button_release_event",
+ GTK_SIGNAL_FUNC(button_event), inst);
+ gtk_signal_connect(GTK_OBJECT(inst->area), "motion_notify_event",
+ GTK_SIGNAL_FUNC(motion_event), inst);
+ gtk_signal_connect(GTK_OBJECT(inst->area), "selection_received",
+ GTK_SIGNAL_FUNC(selection_received), inst);
+ gtk_signal_connect(GTK_OBJECT(inst->area), "selection_get",
+ GTK_SIGNAL_FUNC(selection_get), inst);
+ gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event",
+ GTK_SIGNAL_FUNC(selection_clear), inst);
+ gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",
+ GTK_SIGNAL_FUNC(scrollbar_moved), inst);
gtk_timeout_add(20, timer_func, inst);
gdk_input_add(pty_master_fd, GDK_INPUT_READ, pty_input_func, inst);
gtk_widget_add_events(GTK_WIDGET(inst->area),
- GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK);
gtk_widget_show(inst->area);
+ gtk_widget_show(inst->sbar);
+ gtk_widget_show(GTK_WIDGET(inst->hbox));
gtk_widget_show(window);
inst->textcursor = gdk_cursor_new(GDK_XTERM);