/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
* Copyright (C) 2008 Banco do Brasil S.A.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if GTK_CHECK_VERSION(3,0,0)
#include
#else
#include
#endif
#define WIDTH_IN_PIXELS(terminal,x) (x * cols)
#define HEIGHT_IN_PIXELS(terminal,x) (x * (rows+1))
#define CONTENTS_WIDTH(terminal) (cols * terminal->font.width)
#define CONTENTS_HEIGHT(terminal) (((rows+1) * terminal->font.spacing)+OIA_TOP_MARGIN+2)
/**
* SECTION: v3270
* @title: Virtual 3270 widget
* @short_description: The virtual 3270 terminal widget.
*
* Common functions for interact with the 3270 virtual terminal widget.
*
*/
/// @brief Persistent properties (load/save from session file).
static const gchar *persistent_properties[] = {
"url",
"open-url",
"model-number",
"oversize",
"host-charset",
"unlock-delay",
"color-type",
"host-type",
"crl-preferred-protocol",
"crl_download",
"remap_file",
"dynamic_font_spacing",
"lu_names",
"font_family",
"auto_disconnect",
"colors",
"selection_flags",
"logfile",
"tracefile",
NULL
};
/*--[ Widget definition ]----------------------------------------------------------------------------*/
G_DEFINE_TYPE(v3270, v3270, GTK_TYPE_WIDGET);
/*--[ Globals ]--------------------------------------------------------------------------------------*/
static guint v3270_widget_signal[V3270_SIGNAL_LAST] = { 0 };
/*--[ Prototipes ]-----------------------------------------------------------------------------------*/
static void v3270_realize ( GtkWidget * widget) ;
static void v3270_size_allocate ( GtkWidget * widget,
GtkAllocation * allocation );
static gboolean v3270_focus_in_event(GtkWidget *widget, GdkEventFocus *event);
static gboolean v3270_focus_out_event(GtkWidget *widget, GdkEventFocus *event);
static void v3270_destroy (GtkWidget * object);
/*--[ Implement ]------------------------------------------------------------------------------------*/
static gboolean v3270_popup_menu(GtkWidget * widget)
{
GdkEventButton event;
memset(&event,0,sizeof(event));
event.time = gtk_get_current_event_time();
event.button = 3;
event.type = GDK_BUTTON_PRESS;
v3270_emit_popup( GTK_V3270(widget),
lib3270_get_cursor_address(GTK_V3270(widget)->host),
&event );
return TRUE;
}
void get_preferred_height(GtkWidget *widget, gint *minimum_height, gint *natural_height)
{
int height = GTK_V3270(widget)->minimum_height;
if(minimum_height)
*minimum_height = height ? height : 10;
if(natural_height)
*natural_height = 400;
}
void get_preferred_width(GtkWidget *widget, gint *minimum_width, gint *natural_width)
{
int width = GTK_V3270(widget)->minimum_width;
if(minimum_width)
*minimum_width = width ? width : 10;
if(natural_width)
*natural_width = 600;
}
gboolean v3270_query_tooltip(GtkWidget *widget, gint x, gint y, G_GNUC_UNUSED gboolean keyboard_tooltip, GtkTooltip *tooltip)
{
if(y >= GTK_V3270(widget)->oia.rect->y)
{
GdkRectangle *rect = GTK_V3270(widget)->oia.rect;
if(x >= rect[V3270_OIA_SSL].x && x <= (rect[V3270_OIA_SSL].x + rect[V3270_OIA_SSL].width))
{
H3270 *hSession = GTK_V3270(widget)->host;
if(!lib3270_is_connected(hSession))
{
#ifndef _WIN32
gtk_tooltip_set_icon_from_icon_name(tooltip,"gtk-disconnect",GTK_ICON_SIZE_DIALOG);
#endif // GTK_CHECK_VERSION
gtk_tooltip_set_markup(tooltip,_( "Identity not verified\nDisconnected from host" ) );
}
else
{
#ifndef _WIN32
gtk_tooltip_set_icon_from_icon_name(tooltip,lib3270_get_ssl_state_icon_name(hSession),GTK_ICON_SIZE_DIALOG);
#endif
g_autofree gchar * message =
g_strdup_printf(
"%s\n%s",
lib3270_get_ssl_state_message(hSession),
lib3270_get_ssl_state_description(hSession)
);
gtk_tooltip_set_markup(tooltip,message);
}
return TRUE;
}
}
return FALSE;
}
static void v3270_toggle_changed(G_GNUC_UNUSED v3270 *widget, G_GNUC_UNUSED LIB3270_TOGGLE_ID toggle_id, G_GNUC_UNUSED gboolean toggle_state, G_GNUC_UNUSED const gchar *toggle_name)
{
}
static gboolean delayed_cleanup(H3270 *hSession) {
if(lib3270_get_task_count(hSession))
return G_SOURCE_CONTINUE;
g_message("Delayed cleanup complete");
lib3270_free(hSession);
return G_SOURCE_REMOVE;
}
static void finalize(GObject *object) {
debug("V3270::%s",__FUNCTION__);
v3270 * terminal = GTK_V3270(object);
if(terminal->host) {
// Release session
debug("%s: Cleaning 3270 session",__FUNCTION__);
lib3270_disconnect(terminal->host);
debug("Task count: %u",lib3270_get_task_count(terminal->host));
if(lib3270_get_task_count(terminal->host))
{
// Should wait.
g_message("TN3270 session is busy, delaying cleanup");
lib3270_set_user_data(terminal->host,NULL);
g_idle_add((GSourceFunc) delayed_cleanup,terminal->host);
}
else
{
lib3270_session_free(terminal->host);
}
terminal->host = NULL;
}
if(terminal->remap_filename) {
g_free(terminal->remap_filename);
terminal->remap_filename = NULL;
}
if(terminal->accelerators) {
g_slist_free_full(terminal->accelerators,g_free);
terminal->accelerators = NULL;
}
if(terminal->selection.font_family) {
g_free(terminal->selection.font_family);
terminal->selection.font_family = NULL;
}
if(terminal->selection.color.scheme) {
g_free(terminal->selection.color.scheme);
terminal->selection.color.scheme = NULL;
}
if(terminal->selection.color.value) {
g_free(terminal->selection.color.value);
terminal->selection.color.value = NULL;
}
G_OBJECT_CLASS(v3270_parent_class)->finalize(object);
}
static void v3270_class_init(v3270Class *klass)
{
GObjectClass * gobject_class = G_OBJECT_CLASS(klass);
GtkWidgetClass * widget_class = GTK_WIDGET_CLASS(klass);
GtkBindingSet * binding = gtk_binding_set_by_class(klass);
klass->properties.persistent = persistent_properties;
// Setup widget key bindings
gtk_binding_entry_skip(binding,GDK_F10,0);
// Object methods
gobject_class->finalize = finalize;
// Atoms
klass->clipboard_formatted = gdk_atom_intern_static_string("application/x-v3270-formatted");
// Widget methods
widget_class->realize = v3270_realize;
widget_class->size_allocate = v3270_size_allocate;
widget_class->key_press_event = v3270_key_press_event;
widget_class->key_release_event = v3270_key_release_event;
widget_class->focus_in_event = v3270_focus_in_event;
widget_class->focus_out_event = v3270_focus_out_event;
widget_class->button_press_event = v3270_button_press_event;
widget_class->button_release_event = v3270_button_release_event;
widget_class->motion_notify_event = v3270_motion_notify_event;
widget_class->popup_menu = v3270_popup_menu;
widget_class->scroll_event = v3270_scroll_event;
widget_class->query_tooltip = v3270_query_tooltip;
widget_class->get_preferred_height = get_preferred_height;
widget_class->get_preferred_width = get_preferred_width;
widget_class->destroy = v3270_destroy;
widget_class->draw = v3270_draw;
// Accessibility support
widget_class->get_accessible = v3270_get_accessible;
klass->activate = v3270_activate;
klass->toggle_changed = v3270_toggle_changed;
klass->message_changed = v3270_update_message;
// Register I/O Handlers
v3270_register_io_handlers(klass);
// Cursors
{
#ifdef WIN32
// http://git.gnome.org/browse/gtk+/tree/gdk/win32/gdkcursor-win32.c
// http://www.functionx.com/win32/Lesson02b.htm
static const gchar * cr[LIB3270_POINTER_COUNT] =
{
"ibeam", // LIB3270_POINTER_UNLOCKED
"wait", // LIB3270_POINTER_WAITING
"arrow", // LIB3270_POINTER_LOCKED
"arrow", // LIB3270_POINTER_PROTECTED
"hand", // LIB3270_POINTER_MOVE_SELECTION
"sizenwse", // LIB3270_POINTER_SELECTION_TOP_LEFT
"sizenesw", // LIB3270_POINTER_SELECTION_TOP_RIGHT
"sizens", // LIB3270_POINTER_SELECTION_TOP
"sizenesw", // LIB3270_POINTER_SELECTION_BOTTOM_LEFT
"sizenwse", // LIB3270_POINTER_SELECTION_BOTTOM_RIGHT
"sizens", // LIB3270_POINTER_SELECTION_BOTTOM
"sizewe", // LIB3270_POINTER_SELECTION_LEFT
"sizewe", // LIB3270_POINTER_SELECTION_RIGHT
"help", // LIB3270_POINTER_QUESTION
};
#else
static const int cr[LIB3270_POINTER_COUNT] =
{
GDK_XTERM, // LIB3270_POINTER_UNLOCKED
GDK_WATCH, // LIB3270_POINTER_WAITING
GDK_X_CURSOR, // LIB3270_POINTER_LOCKED
GDK_ARROW, // LIB3270_POINTER_PROTECTED
GDK_HAND1, // LIB3270_POINTER_MOVE_SELECTION
GDK_TOP_LEFT_CORNER, // LIB3270_POINTER_SELECTION_TOP_LEFT
GDK_TOP_RIGHT_CORNER, // LIB3270_POINTER_SELECTION_TOP_RIGHT
GDK_TOP_SIDE, // LIB3270_POINTER_SELECTION_TOP
GDK_BOTTOM_LEFT_CORNER, // LIB3270_POINTER_SELECTION_BOTTOM_LEFT
GDK_BOTTOM_RIGHT_CORNER, // LIB3270_POINTER_SELECTION_BOTTOM_RIGHT
GDK_BOTTOM_SIDE, // LIB3270_POINTER_SELECTION_BOTTOM
GDK_LEFT_SIDE, // LIB3270_POINTER_SELECTION_LEFT
GDK_RIGHT_SIDE, // LIB3270_POINTER_SELECTION_RIGHT
GDK_QUESTION_ARROW, // LIB3270_POINTER_QUESTION
};
#endif // WIN32
int f;
for(f=0;fcursors[f] = gdk_cursor_new_from_name(gdk_display_get_default(),cr[f]);
#else
klass->cursors[f] = gdk_cursor_new_for_display(gdk_display_get_default(),cr[f]);
#endif
}
}
// Signals
widget_class->activate_signal =
g_signal_new( I_("activate"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (v3270Class, activate),
NULL, NULL,
v3270_VOID__VOID,
G_TYPE_NONE, 0);
v3270_widget_signal[V3270_SIGNAL_TOGGLE_CHANGED] =
g_signal_new( I_("toggle_changed"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (v3270Class, toggle_changed),
NULL, NULL,
v3270_VOID__VOID_ENUM_BOOLEAN_STRING,
G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_BOOLEAN, G_TYPE_STRING);
v3270_widget_signal[V3270_SIGNAL_MESSAGE_CHANGED] =
g_signal_new( I_("message_changed"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (v3270Class, message_changed),
NULL, NULL,
v3270_VOID__VOID_ENUM,
G_TYPE_NONE, 1, G_TYPE_UINT);
v3270_widget_signal[V3270_SIGNAL_KEYPRESS] =
g_signal_new( I_("keypress"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
v3270_BOOLEAN__UINT_ENUM,
G_TYPE_BOOLEAN, 2, G_TYPE_UINT, G_TYPE_UINT);
v3270_widget_signal[V3270_SIGNAL_CONNECTED] =
g_signal_new( I_("connected"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING);
v3270_widget_signal[V3270_SIGNAL_DISCONNECTED] =
g_signal_new( I_("disconnected"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID,
G_TYPE_NONE, 0);
v3270_widget_signal[V3270_SIGNAL_SESSION_CHANGED] =
g_signal_new( I_("session_changed"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID,
G_TYPE_NONE, 0);
v3270_widget_signal[V3270_SIGNAL_MODEL_CHANGED] =
g_signal_new( I_("model_changed"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID_UINT_STRING,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
v3270_widget_signal[V3270_SIGNAL_SELECTING] =
g_signal_new( I_("selecting"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID_BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
v3270_widget_signal[V3270_SIGNAL_POPUP] =
g_signal_new( I_("popup"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
v3270_BOOLEAN__VOID_BOOLEAN_BOOLEAN_POINTER,
G_TYPE_BOOLEAN, 3, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER);
v3270_widget_signal[V3270_SIGNAL_OIA_POPUP] =
g_signal_new( I_("oia-popup"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
v3270_BOOLEAN__VOID_UINT_POINTER,
G_TYPE_BOOLEAN, 2, G_TYPE_UINT, G_TYPE_POINTER);
v3270_widget_signal[V3270_SIGNAL_PASTENEXT] =
g_signal_new( I_("pastenext"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID_BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
v3270_widget_signal[V3270_SIGNAL_CLIPBOARD] =
g_signal_new( I_("has_text"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID_BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
v3270_widget_signal[V3270_SIGNAL_CHANGED] =
g_signal_new( I_("changed"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID_UINT_UINT,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
v3270_widget_signal[V3270_SIGNAL_FIELD] =
g_signal_new( I_("field_clicked"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
v3270_BOOLEAN__VOID_BOOLEAN_UINT_POINTER,
G_TYPE_BOOLEAN, 3, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_POINTER);
v3270_widget_signal[V3270_SIGNAL_PRINT_DONE] =
g_signal_new( I_("print-done"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID_POINTER_UINT,
G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT, 0);
v3270_widget_signal[V3270_SIGNAL_PRINT_SETUP] =
g_signal_new( I_("print-setup"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID_POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER, 0);
v3270_widget_signal[V3270_SIGNAL_SAVE_SETTINGS] =
g_signal_new( I_("save-settings"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
v3270_VOID__VOID,
G_TYPE_NONE, 0);
v3270_widget_signal[V3270_SIGNAL_LOAD_POPUP_RESPONSE] =
g_signal_new( I_("load-popup-response"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
v3270_UINT__STRING,
G_TYPE_UINT, 1, G_TYPE_STRING);
v3270_widget_signal[V3270_SIGNAL_FIRE_ACTION] =
g_signal_new( I_("fire-action"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
v3270_UINT__STRING,
G_TYPE_UINT, 1, G_TYPE_STRING);
v3270_widget_signal[V3270_SIGNAL_OPEN_URL] =
g_signal_new( I_("open-url"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
v3270_UINT__STRING,
G_TYPE_UINT, 1, G_TYPE_STRING);
v3270_widget_signal[V3270_SIGNAL_SAVE_POPUP_RESPONSE] =
g_signal_new( I_("save-popup-response"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
v3270_BOOLEAN__POINTER_UINT,
G_TYPE_BOOLEAN, 2, G_TYPE_POINTER, G_TYPE_UINT);
v3270_init_properties(gobject_class);
}
static gboolean activity_tick(v3270 *widget)
{
if(widget->activity.disconnect && lib3270_is_connected(widget->host) && ((guint) ((time(0) - widget->activity.timestamp)/60)) >= widget->activity.disconnect)
lib3270_disconnect(widget->host);
return TRUE;
}
static void release_activity_timer(v3270 *widget)
{
widget->activity.timer = NULL;
}
static void v3270_init(v3270 *widget)
{
// size_t ix;
widget->host = lib3270_session_new(NULL);
lib3270_set_user_data(widget->host,widget);
lib3270_set_url(widget->host,NULL);
// Install callbacks
v3270_install_callbacks(widget);
// Setup clipboard.
widget->selection.target = GDK_SELECTION_CLIPBOARD;
widget->selection.options = V3270_SELECTION_DEFAULT;
// Reset timer
widget->activity.timestamp = time(0);
widget->activity.disconnect = 0;
// Setup input method
widget->input_method = gtk_im_multicontext_new();
g_signal_connect(G_OBJECT(widget->input_method),"commit",G_CALLBACK(v3270_key_commit),widget);
#if GTK_CHECK_VERSION(2,18,0)
gtk_widget_set_can_default(GTK_WIDGET(widget),TRUE);
gtk_widget_set_can_focus(GTK_WIDGET(widget),TRUE);
#else
GTK_WIDGET_SET_FLAGS(GTK_WIDGET(widget),(GTK_CAN_DEFAULT|GTK_CAN_FOCUS));
#endif // GTK(2,18)
// Setup widget
gtk_widget_add_events(GTK_WIDGET(widget),GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_MOTION_MASK|GDK_BUTTON_RELEASE_MASK|GDK_POINTER_MOTION_MASK|GDK_ENTER_NOTIFY_MASK|GDK_SCROLL_MASK);
gtk_widget_set_has_tooltip(GTK_WIDGET(widget),TRUE);
// Setup auto disconnect timer
widget->cursor.timer = NULL;
// Enable drawing
widget->drawing = 1;
// Set defaults
v3270_font_info_init(&widget->font);
v3270_set_color_table(widget->color,v3270_get_default_colors());
// Init accelerators
widget->accelerators = v3270_accelerator_map_load_default(NULL);
#ifdef DEBUG
lib3270_testpattern(widget->host);
#endif // DEBUG
}
LIB3270_EXPORT GtkWidget * v3270_new(void)
{
return g_object_new(GTK_TYPE_V3270, NULL);
}
static void v3270_destroy(GtkWidget *widget)
{
v3270 * terminal = GTK_V3270(widget);
debug("%s",__FUNCTION__);
if(terminal->host)
{
// Cleanup
lib3270_reset_callbacks(terminal->host);
lib3270_set_user_data(terminal->host,NULL);
debug("%s: disconnecting", __FUNCTION__);
lib3270_disconnect(terminal->host);
}
if(terminal->accessible)
{
gtk_accessible_set_widget(terminal->accessible, NULL);
g_object_unref(terminal->accessible);
terminal->accessible = NULL;
}
v3270_font_info_unset(&terminal->font);
if(terminal->surface)
{
cairo_surface_destroy(terminal->surface);
terminal->surface = NULL;
}
if(terminal->cursor.surface)
{
cairo_surface_destroy(terminal->cursor.surface);
terminal->cursor.surface = NULL;
}
if(terminal->timer)
{
g_source_destroy(terminal->timer);
while(terminal->timer)
g_source_unref(terminal->timer);
}
if(terminal->blink.timer)
{
g_source_destroy(terminal->blink.timer);
while(terminal->blink.timer)
g_source_unref(terminal->blink.timer);
}
if(terminal->cursor.timer)
{
g_source_destroy(terminal->cursor.timer);
while(terminal->cursor.timer)
g_source_unref(terminal->cursor.timer);
}
if(terminal->activity.timer)
{
g_source_destroy(terminal->activity.timer);
while(terminal->activity.timer)
g_source_unref(terminal->activity.timer);
}
if(terminal->input_method)
{
g_object_unref(terminal->input_method);
terminal->input_method = NULL;
}
v3270_clear_selection(terminal);
if(terminal->session.name)
{
g_free(terminal->session.name);
terminal->session.name = NULL;
}
if(terminal->session.title)
{
g_free(terminal->session.title);
terminal->session.title = NULL;
}
GTK_WIDGET_CLASS(v3270_parent_class)->destroy(widget);
}
static gboolean timer_tick(v3270 *widget)
{
if(lib3270_get_toggle(widget->host,LIB3270_TOGGLE_CURSOR_BLINK))
{
widget->cursor.show ^= 1;
v3270_queue_draw_area(GTK_WIDGET(widget), widget->cursor.rect.x,
widget->cursor.rect.y,
widget->cursor.rect.width,
widget->cursor.rect.height );
}
return TRUE;
}
static void release_cursor_timer(v3270 *widget)
{
widget->cursor.timer = NULL;
}
static gboolean bg_pos_realize(v3270 *terminal)
{
if(lib3270_get_toggle(terminal->host,LIB3270_TOGGLE_FULL_SCREEN))
gtk_window_fullscreen(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(terminal))));
if(lib3270_get_toggle(terminal->host,LIB3270_TOGGLE_CONNECT_ON_STARTUP) && lib3270_is_disconnected(terminal->host))
v3270_reconnect(GTK_WIDGET(terminal));
return FALSE;
}
static void v3270_realize(GtkWidget * widget)
{
if(!gtk_widget_get_has_window(widget))
{
GTK_WIDGET_CLASS(v3270_parent_class)->realize(widget);
}
else
{
GtkAllocation allocation;
GdkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
gtk_widget_set_realized (widget, TRUE);
gtk_widget_get_allocation (widget, &allocation);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
window = gdk_window_new(gtk_widget_get_parent_window (widget),&attributes, attributes_mask);
gdk_window_set_user_data (window, widget);
gtk_widget_set_window(widget, window);
gtk_im_context_set_client_window(GTK_V3270(widget)->input_method,window);
g_idle_add((GSourceFunc) bg_pos_realize, GTK_V3270(widget));
}
v3270_reconfigure(GTK_V3270(widget));
if(!GTK_V3270(widget)->cursor.timer)
{
// Setup cursor blink timer
v3270 *terminal = GTK_V3270(widget);
terminal->cursor.timer = g_timeout_source_new(500);
g_source_set_callback(terminal->cursor.timer,(GSourceFunc) timer_tick, widget, (GDestroyNotify) release_cursor_timer);
g_source_attach(terminal->cursor.timer, NULL);
g_source_unref(terminal->cursor.timer);
}
if(!GTK_V3270(widget)->activity.timer)
{
v3270 *terminal = GTK_V3270(widget);
terminal->activity.timer = g_timeout_source_new(10000);
g_source_set_callback(terminal->activity.timer,(GSourceFunc) activity_tick, widget, (GDestroyNotify) release_activity_timer);
g_source_attach(terminal->activity.timer, NULL);
g_source_unref(terminal->activity.timer);
}
}
static void v3270_size_allocate(GtkWidget * widget, GtkAllocation * allocation)
{
g_return_if_fail(GTK_IS_V3270(widget));
g_return_if_fail(allocation != NULL);
gtk_widget_set_allocation(widget, allocation);
if(gtk_widget_get_realized(widget))
{
if(gtk_widget_get_has_window(widget))
gdk_window_move_resize(gtk_widget_get_window (widget),allocation->x, allocation->y,allocation->width, allocation->height);
v3270_reconfigure(GTK_V3270(widget));
}
}
#if ! GTK_CHECK_VERSION(2,18,0)
G_GNUC_INTERNAL void gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation)
{
*allocation = widget->allocation;
}
#endif // !GTK(2,18)
static gboolean notify_focus(GtkWidget *widget, GdkEventFocus *event)
{
GtkAccessible *obj = GTK_V3270(widget)->accessible;
if(obj)
g_signal_emit_by_name (obj, "focus-event", event->in);
return FALSE;
}
gboolean v3270_focus_in_event(GtkWidget *widget, GdkEventFocus *event)
{
v3270 * terminal = GTK_V3270(widget);
gtk_im_context_focus_in(terminal->input_method);
return notify_focus(widget,event);
}
gboolean v3270_focus_out_event(GtkWidget *widget, GdkEventFocus *event)
{
v3270 * terminal = GTK_V3270(widget);
gtk_im_context_focus_out(terminal->input_method);
#ifdef KEY_FLAG_ALT
terminal->keyflags &= ~KEY_FLAG_ALT;
v3270_draw_alt_status(terminal);
#endif // KEY_FLAG_ALT
return notify_focus(widget,event);
}
const GtkWidgetClass * v3270_get_parent_class(void)
{
return GTK_WIDGET_CLASS(v3270_parent_class);
}
LIB3270_EXPORT GtkIMContext * v3270_get_im_context(GtkWidget *widget)
{
return GTK_V3270(widget)->input_method;
}
static gboolean bg_emit_save_settings(v3270 *terminal)
{
GdkWindow * window = gtk_widget_get_window(GTK_WIDGET(terminal));
if(window) {
gdk_window_set_cursor(
window,
GTK_V3270_GET_CLASS(terminal)->cursors[LIB3270_POINTER_WAITING]
);
gdk_display_sync(gtk_widget_get_display(GTK_WIDGET(terminal)));
terminal->freeze = 0;
g_signal_emit(terminal,v3270_widget_signal[V3270_SIGNAL_SAVE_SETTINGS], 0, FALSE);
gdk_window_set_cursor(
window,
NULL
);
} else {
terminal->freeze = 0;
g_signal_emit(terminal,v3270_widget_signal[V3270_SIGNAL_SAVE_SETTINGS], 0, FALSE);
}
g_object_unref(terminal);
return FALSE;
}
LIB3270_EXPORT void v3270_emit_save_settings(GtkWidget *widget, const gchar *property_name)
{
if(property_name)
g_object_notify(G_OBJECT(widget),property_name);
debug("%s(Freeze is %s)",__FUNCTION__,GTK_V3270(widget)->freeze ? "ON" : "OFF");
if(widget && GTK_IS_V3270(widget) && !GTK_V3270(widget)->freeze)
{
g_object_ref(widget);
GTK_V3270(widget)->freeze = 1;
g_idle_add((GSourceFunc) bg_emit_save_settings, G_OBJECT(widget));
}
}
void v3270_signal_emit(gpointer instance, enum V3270_SIGNAL signal_id, ...)
{
va_list var_args;
va_start (var_args, signal_id);
g_signal_emit_valist(instance, v3270_widget_signal[signal_id], 0, var_args);
va_end (var_args);
}