/* 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 #ifdef _WIN32 #include #endif // _WIN32 #include #include #include #include #include #include #include #include #include #include #include /*--[ Implement ]------------------------------------------------------------------------------------*/ struct has_timer { H3270 * hSession; unsigned char on; }; static gboolean bg_has_timer(struct has_timer *data) { GtkWidget *widget = GTK_WIDGET(lib3270_get_user_data(data->hSession)); if(data->on) v3270_start_timer(widget); else v3270_stop_timer(widget); g_free(data); return FALSE; } static void set_timer(H3270 *session, unsigned char on) { struct has_timer *data = g_malloc0(sizeof(struct has_timer)); data->hSession = session; data->on = on; g_idle_add((GSourceFunc) bg_has_timer, data); } static void update_toggle(H3270 *session, LIB3270_TOGGLE_ID id, unsigned char value, G_GNUC_UNUSED LIB3270_TOGGLE_TYPE reason, const char *name) { v3270_update_toggle((GtkWidget *) lib3270_get_user_data(session), id, value, name); } struct update_message_data { H3270 *hSession; LIB3270_MESSAGE message; }; static gboolean bg_update_message(struct update_message_data *data) { v3270 *terminal = (v3270 *) lib3270_get_user_data(data->hSession); v3270_signal_emit( terminal, V3270_SIGNAL_MESSAGE_CHANGED, (gint) data->message ); g_free(data); return FALSE; } static void update_message(H3270 *hSession, LIB3270_MESSAGE message) { struct update_message_data *data = g_new0(struct update_message_data,1); data->hSession = hSession; data->message = message; g_idle_add((GSourceFunc) bg_update_message, data); } static void update_luname(H3270 *session, const char G_GNUC_UNUSED(*name)) { g_idle_add((GSourceFunc) v3270_update_associated_lu, lib3270_get_user_data(session)); } static gboolean v3270_update_url(v3270 *terminal) { GtkWidget * widget = GTK_WIDGET(terminal); debug("url=%s",v3270_get_url(widget)); v3270_emit_save_settings(widget,"url"); v3270_signal_emit(widget, V3270_SIGNAL_SESSION_CHANGED); return FALSE; } static void update_url(H3270 *session, const char G_GNUC_UNUSED(*name)) { g_idle_add((GSourceFunc) v3270_update_url, lib3270_get_user_data(session)); } struct select_cursor_data { H3270 * hSession; LIB3270_POINTER id; }; static gboolean bg_select_cursor(struct select_cursor_data *data) { GtkWidget *widget = GTK_WIDGET(lib3270_get_user_data(data->hSession)); #if GTK_CHECK_VERSION(2,20,0) if(gtk_widget_get_realized(widget) && gtk_widget_get_has_window(widget)) #else if(GTK_WIDGET_REALIZED(widget) && widget->window) #endif // GTK(2,20) { GTK_V3270(widget)->pointer_id = data->id; v3270_update_mouse_pointer(widget); } return FALSE; } static void select_cursor(H3270 *session, LIB3270_POINTER id) { struct select_cursor_data *data = g_new0(struct select_cursor_data,1); data->hSession = session; data->id = id; g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,(GSourceFunc) bg_select_cursor, data, g_free); } static void ctlr_done(H3270 *session) { GtkWidget *widget = GTK_WIDGET(lib3270_get_user_data(session)); if(gtk_widget_get_realized(widget) && gtk_widget_get_has_window(widget)) { v3270_update_mouse_pointer(widget); } } static void update_connect(H3270 *session, unsigned char connected) { v3270 *widget = GTK_V3270(lib3270_get_user_data(session)); if(connected) { widget->cursor.show |= 2; v3270_signal_emit(GTK_WIDGET(widget), V3270_SIGNAL_CONNECTED, lib3270_get_url(session)); } else { widget->cursor.show &= ~2; v3270_signal_emit(GTK_WIDGET(widget), V3270_SIGNAL_DISCONNECTED); } debug("%s(%p)",__FUNCTION__,GTK_V3270_GET_CLASS(widget)->properties.online); g_object_notify_by_pspec(G_OBJECT(widget), GTK_V3270_GET_CLASS(widget)->properties.online); widget->activity.timestamp = time(0); gtk_widget_queue_draw(GTK_WIDGET(widget)); } static void update_screen_size(H3270 *session, G_GNUC_UNUSED unsigned short rows, G_GNUC_UNUSED unsigned short cols) { debug("%s",__FUNCTION__); GtkWidget * widget = GTK_WIDGET(lib3270_get_user_data(session)); v3270_reconfigure(GTK_V3270(widget)); gtk_widget_queue_draw(widget); } static void update_model(H3270 *session, const char *name, int model, G_GNUC_UNUSED int rows, G_GNUC_UNUSED int cols) { GtkWidget * widget = GTK_WIDGET(lib3270_get_user_data(session)); // g_object_notify_by_pspec(G_OBJECT(widget), GTK_V3270_GET_CLASS(widget)->properties.settings[V3270_SETTING_MODEL_NUMBER]); v3270_signal_emit(widget,V3270_SIGNAL_MODEL_CHANGED, (guint) model, name); } static void changed(H3270 *session, int offset, int len) { GtkWidget * widget = lib3270_get_user_data(session); GtkAccessible * obj = GTK_V3270(widget)->accessible; if(obj) { // Get new text, notify atk gsize bytes_written = 0; char * text = lib3270_get_string_at_address(session,offset,len,'\n'); if(text) { GError * error = NULL; gchar * utfchar = g_convert_with_fallback( text, -1, "UTF-8", lib3270_get_display_charset(session), " ", NULL, &bytes_written, &error ); lib3270_free(text); if(error) { g_warning("%s failed: %s",__FUNCTION__,error->message); g_error_free(error); } if(utfchar) { g_signal_emit_by_name(obj, "text-insert", offset, bytes_written, utfchar); g_free(utfchar); } } } #ifdef WIN32 gtk_widget_queue_draw(widget); #endif // WIN32 v3270_signal_emit(GTK_WIDGET(widget),V3270_SIGNAL_CHANGED, (guint) offset, (guint) len); } static void set_selection(H3270 *session, unsigned char status) { GtkWidget * widget = GTK_WIDGET(lib3270_get_user_data(session)); debug("%s(%p)",__FUNCTION__,GTK_V3270_GET_CLASS(widget)->properties.selection); g_object_notify_by_pspec(G_OBJECT(widget), GTK_V3270_GET_CLASS(widget)->properties.selection); v3270_signal_emit(widget,V3270_SIGNAL_SELECTING, status ? TRUE : FALSE); } static void update_selection(H3270 *session, G_GNUC_UNUSED int start, G_GNUC_UNUSED int end) { // Selected region changed GtkWidget * widget = GTK_WIDGET(lib3270_get_user_data(session)); GtkAccessible * atk_obj = GTK_V3270(widget)->accessible; if(atk_obj) g_signal_emit_by_name(atk_obj,"text-selection-changed"); } static int print(H3270 *session, LIB3270_CONTENT_OPTION mode) { return v3270_print_dialog(GTK_WIDGET(lib3270_get_user_data(session)), mode, NULL); } static int save(H3270 *session, LIB3270_CONTENT_OPTION mode, const char *filename) { GtkWidget * widget = GTK_WIDGET(lib3270_get_user_data(session)); if(!GTK_IS_V3270(widget)) return errno = EINVAL; GtkWidget *dialog = v3270_save_dialog_new(widget,mode,filename); gtk_widget_show_all(dialog); v3270_save_dialog_run(dialog); gtk_widget_destroy(dialog); return 0; } static int load(H3270 *session, const char *filename) { GtkWidget * widget = GTK_WIDGET(lib3270_get_user_data(session)); if(!GTK_IS_V3270(widget)) return errno = EINVAL; GtkWidget *dialog = v3270_load_dialog_new(widget,filename); gtk_widget_show_all(dialog); v3270_load_dialog_run(dialog); gtk_widget_destroy(dialog); return 0; } static gboolean bg_update_ssl(H3270 *session) { v3270 *terminal = GTK_V3270(lib3270_get_user_data(session)); if(terminal->surface) { // Redraw SSL area. GdkRectangle * r; cairo_t * cr = v3270_oia_set_update_region(terminal,&r,V3270_OIA_SSL); v3270_draw_ssl_status(terminal,cr,r); v3270_queue_draw_area(GTK_WIDGET(terminal),r->x,r->y,r->width,r->height); cairo_destroy(cr); } if(v3270_blink_ssl(terminal)) v3270_start_blinking(GTK_WIDGET(lib3270_get_user_data(session))); return FALSE; } static void update_ssl(H3270 *session, G_GNUC_UNUSED LIB3270_SSL_STATE state) { g_idle_add((GSourceFunc) bg_update_ssl, session); } struct update_oia_data { H3270 *session; LIB3270_FLAG id; unsigned char on; }; static gboolean bg_update_oia(struct update_oia_data *data) { v3270_update_oia(GTK_V3270(lib3270_get_user_data(data->session)), data->id, data->on); return FALSE; } static void update_oia(H3270 *session, LIB3270_FLAG id, unsigned char on) { struct update_oia_data *data = g_new0(struct update_oia_data,1); data->session = session; data->id = id; data->on = on; g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,(GSourceFunc) bg_update_oia, data, g_free); } static int popup(H3270 *hSession, const LIB3270_POPUP *popup, unsigned char wait) { GtkResponseType response = v3270_popup_dialog_show( GTK_WIDGET(lib3270_get_user_data(hSession)), popup, wait != 0); if(response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY) return 0; return errno = ECANCELED; } static int action(H3270 *hSession, const char *name) { guint response = ENOENT; v3270_signal_emit( GTK_WIDGET(lib3270_get_user_data(hSession)), V3270_SIGNAL_FIRE_ACTION, name, &response ); return response; } struct _bg_reconnect { H3270 *hSession; int seconds; }; static gboolean bg_reconnect(struct _bg_reconnect *cfg) { lib3270_reconnect(cfg->hSession,cfg->seconds); return G_SOURCE_REMOVE; } static int reconnect(H3270 *hSession,int seconds) { struct _bg_reconnect *cfg = g_new0(struct _bg_reconnect, 1); cfg->hSession = hSession; cfg->seconds = seconds; g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,(GSourceFunc) bg_reconnect, cfg, g_free); return 0; } struct _word_selected { H3270 *hSession; int offset; int len; }; static gboolean bg_word_selected(struct _word_selected *cfg) { v3270 *terminal = (v3270 *) lib3270_get_user_data(cfg->hSession); debug("%s(open-url=%d)",__FUNCTION__,terminal->open_url); if(cfg->len > 3 && terminal->open_url) { lib3270_autoptr(char) text = lib3270_get_string_at_address(cfg->hSession,cfg->offset,cfg->len,0); if(text && (g_str_has_prefix(text,"http://") || g_str_has_prefix(text,"https://")) ) { debug("Emitting '%s'", text); guint response = 0; v3270_signal_emit( terminal, V3270_SIGNAL_OPEN_URL, text, &response ); debug("Response was: %d", (int) response); if(response == 0) { // No one has changed the response, take default action. g_message("Opening '%s'",text); #ifdef G_OS_WIN32 if(gtk_show_uri_on_window( NULL, text, GDK_CURRENT_TIME, NULL )) { v3270_unselect(GTK_WIDGET(terminal)); } #else if(gtk_show_uri_on_window( GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(terminal))), text, GDK_CURRENT_TIME, NULL )) { v3270_unselect(GTK_WIDGET(terminal)); } #endif // G_OS_WIN32 } } } return G_SOURCE_REMOVE; } static void word_selected(H3270 *hSession, int from, int to) { struct _word_selected *cfg = g_new0(struct _word_selected, 1); cfg->hSession = hSession; cfg->offset = from; cfg->len = (to-from)+1; g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,(GSourceFunc) bg_word_selected, cfg, g_free); } void v3270_install_callbacks(v3270 *widget) { struct lib3270_session_callbacks *cbk; cbk = lib3270_get_session_callbacks(widget->host,G_STRINGIFY(LIB3270_REVISION),sizeof(struct lib3270_session_callbacks)); if(!cbk) { if(g_ascii_strcasecmp(G_STRINGIFY(LIB3270_REVISION),lib3270_get_revision())) { g_error( _("Invalid callback table, the release %s of lib%s can't be used (expecting revision %s)"), lib3270_get_revision(), G_STRINGIFY(LIB3270_NAME), G_STRINGIFY(LIB3270_REVISION) ); } else { g_error( _("Unexpected callback table, the release %s of lib%s is invalid"), lib3270_get_revision(), G_STRINGIFY(LIB3270_NAME) ); } return; } cbk->update = v3270_update_char; cbk->changed = changed; cbk->set_timer = set_timer; cbk->set_selection = set_selection; cbk->update_selection = update_selection; cbk->update_luname = update_luname; cbk->update_url = update_url; cbk->configure = update_screen_size; cbk->update_status = update_message; cbk->update_cursor = v3270_update_cursor; cbk->update_toggle = update_toggle; cbk->update_oia = update_oia; cbk->cursor = select_cursor; cbk->update_connect = update_connect; cbk->update_model = update_model; cbk->changed = changed; cbk->ctlr_done = ctlr_done; cbk->update_ssl = update_ssl; cbk->print = print; cbk->save = save; cbk->load = load; cbk->popup = popup; cbk->action = action; cbk->reconnect = reconnect; cbk->word_selected = word_selected; }