/* * "Software pw3270, desenvolvido com base nos códigos fontes do WC3270 e X3270 * (Paul Mattes Paul.Mattes@usa.net), de emulação de terminal 3270 para acesso a * aplicativos mainframe. Registro no INPI sob o nome G3270. * * Copyright (C) <2008> * * Este programa é software livre. Você pode redistribuí-lo e/ou modificá-lo sob * os termos da GPL v.2 - Licença Pública Geral GNU, conforme publicado pela * Free Software Foundation. * * Este programa é distribuído na expectativa de ser útil, mas SEM QUALQUER * GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de ADEQUAÇÃO * A QUALQUER PROPÓSITO EM PARTICULAR. Consulte a Licença Pública Geral GNU para * obter mais detalhes. * * Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este * programa; se não, escreva para a Free Software Foundation, Inc., 51 Franklin * St, Fifth Floor, Boston, MA 02110-1301 USA * * Este programa está nomeado como trace.c e possui - linhas de código. * * Contatos: * * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) * */ /** * SECTION:V3270Trace * @Short_description: A trace monitor widget. * @Title: V3270Trace * * The #V3270Trace shows a text area with the lib3270 trace output. * */ #include #define ENABLE_NLS #define GETTEXT_PACKAGE PACKAGE_NAME #include #include #include #include #include #include #include #include #include #include #if defined( HAVE_SYSLOG ) #include #endif // HAVE_SYSLOG /*--[ Widget definition ]----------------------------------------------------------------------------*/ G_BEGIN_DECLS struct _V3270TraceClass { GtkBoxClass parent_class; }; struct _V3270Trace { GtkBox parent; H3270 * hSession; ///< @brief TN3270 Session. GtkWidget * terminal; ///< @brief V3270 Widget. GtkScrolledWindow * scroll; GtkTextView * view; ///< @brief Text view; GtkTextBuffer * text; ///< @brief Trace window contents. GtkEntry * entry; ///< @brief Command line entry. GtkWidget * buttons; ///< @brief Button bar. gchar * filename; ///< @brief Selected file name. guint log_handler; ///< @brief GTK Log Handler. /// @brief lib3270's saved trace handler. struct { void (*handler)(H3270 *session, void *userdata, const char *fmt, va_list args); void *userdata; } trace; }; G_END_DECLS G_DEFINE_TYPE(V3270Trace, V3270Trace, GTK_TYPE_BOX); /*--[ Implement ]------------------------------------------------------------------------------------*/ static void trace_handler(H3270 *hSession, void *userdata, const char *fmt, va_list args) { g_autofree gchar *ptr = g_strdup_vprintf(fmt,args); g_autofree gchar * utftext = g_convert_with_fallback(ptr,-1,"UTF-8",lib3270_get_display_charset(hSession),"?",NULL,NULL,NULL); v3270_trace_append_text(GTK_WIDGET(userdata),utftext); } static void set_session(V3270Trace *widget, H3270 *hSession) { // Return if it's the same session. if(widget->hSession == hSession) return; debug("%s: Session changes %p -> %p", __FUNCTION__, widget->hSession, hSession); if(widget->hSession) { lib3270_set_trace_handler(widget->hSession,widget->trace.handler,widget->trace.userdata); } widget->hSession = hSession; if(hSession) { lib3270_get_trace_handler(hSession,&widget->trace.handler,&widget->trace.userdata); lib3270_set_trace_handler(hSession,trace_handler,(void *) widget); } } static void finalize(GObject *object) { debug("V3270Trace::%s",__FUNCTION__); V3270Trace *trace = GTK_V3270_TRACE(object); if(trace->filename) { g_free(trace->filename); trace->filename = NULL; } if(trace->log_handler) { g_log_remove_handler(NULL,trace->log_handler); trace->log_handler = 0; } set_session(trace,NULL); g_clear_object(&trace->terminal); G_OBJECT_CLASS(V3270Trace_parent_class)->finalize(object); } static void V3270Trace_class_init(G_GNUC_UNUSED V3270TraceClass *klass) { G_OBJECT_CLASS(klass)->finalize = finalize; } static void v3270_trace_execute(GtkWidget *widget, const gchar *cmd) { if(!*cmd) return; v3270_trace_printf(widget, "%s\n",cmd); V3270Trace *trace = GTK_V3270_TRACE(widget); if(trace->terminal) { int rc = v3270_exec_command(trace->terminal,cmd); if(rc) v3270_trace_printf(widget, "rc=%d (%s)\n",rc,strerror(rc)); } else { v3270_trace_append_text(widget, "Can't execute command without an associated terminal"); } gtk_entry_set_text(trace->entry, ""); } static void execute_command(GtkEntry *entry, G_GNUC_UNUSED GtkEntryIconPosition icon_pos, G_GNUC_UNUSED GdkEvent *event, GtkWidget *widget) { v3270_trace_execute(widget, gtk_entry_get_text(entry)); } static void entry_activated(GtkEntry *entry, GtkWidget *widget) { v3270_trace_execute(widget, gtk_entry_get_text(entry)); } static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, GtkWidget *widget) { #ifndef LOG_INFO #define LOG_INFO 0 #endif // LOG_INFO #ifndef LOG_ERR #define LOG_ERR 0 #endif // LOG_ERR #ifndef LOG_DEBUG #define LOG_DEBUG 0 #endif // LOG_DEBUG static const struct _logtype { GLogLevelFlags log_level; const gchar * msg; } logtype[] = { { G_LOG_FLAG_RECURSION, "recursion" }, { G_LOG_FLAG_FATAL, "fatal error" }, /* GLib log levels */ { G_LOG_LEVEL_ERROR, "error" }, { G_LOG_LEVEL_CRITICAL, "critical error" }, { G_LOG_LEVEL_WARNING, "warning" }, { G_LOG_LEVEL_MESSAGE, "message" }, { G_LOG_LEVEL_INFO, "info" }, { G_LOG_LEVEL_DEBUG, "debug" }, }; size_t f; for(f=0;fbuttons = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); // https://developer.gnome.org/hig/stable/visual-layout.html.en gtk_container_set_border_width(GTK_CONTAINER(widget->buttons), 12); gtk_box_set_spacing(GTK_BOX(widget->buttons),12); gtk_button_box_set_layout(GTK_BUTTON_BOX(widget->buttons), GTK_BUTTONBOX_START); gtk_widget_set_valign(widget->buttons,GTK_ALIGN_START); gtk_box_pack_start(GTK_BOX(widget),widget->buttons,FALSE,FALSE,0); } // Create text view { widget->scroll = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL,NULL)); gtk_scrolled_window_set_policy(widget->scroll,GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); gtk_widget_set_vexpand(GTK_WIDGET(widget->scroll),TRUE); gtk_widget_set_hexpand(GTK_WIDGET(widget->scroll),TRUE); gtk_box_pack_start(GTK_BOX(widget),GTK_WIDGET(widget->scroll),TRUE,TRUE,4); widget->view = GTK_TEXT_VIEW(gtk_text_view_new()); #if GTK_CHECK_VERSION(3,16,0) gtk_text_view_set_monospace(widget->view,TRUE); #endif // GTK_CHECK_VERSION widget->text = gtk_text_view_get_buffer(widget->view); gtk_text_view_set_editable(widget->view, TRUE); gtk_container_add(GTK_CONTAINER(widget->scroll),GTK_WIDGET(widget->view)); gtk_widget_set_can_default(GTK_WIDGET(widget->view),FALSE); } // Create command line { widget->entry = GTK_ENTRY(gtk_entry_new()); gtk_widget_set_can_default(GTK_WIDGET(widget->entry),TRUE); gtk_widget_grab_focus(GTK_WIDGET(widget->entry)); gtk_entry_set_activates_default(widget->entry,TRUE); gtk_widget_set_vexpand(GTK_WIDGET(widget->entry),FALSE); gtk_widget_set_hexpand(GTK_WIDGET(widget->entry),TRUE); gtk_entry_set_icon_from_icon_name(widget->entry,GTK_ENTRY_ICON_SECONDARY,"system-run"); gtk_entry_set_placeholder_text(widget->entry,_("Command to execute")); gtk_box_pack_end(GTK_BOX(widget),GTK_WIDGET(widget->entry),FALSE,FALSE,4); g_signal_connect(G_OBJECT(widget->entry),"icon-press",G_CALLBACK(execute_command),widget); g_signal_connect(G_OBJECT(widget->entry),"activate",G_CALLBACK(entry_activated),widget); } // Grab GTK messages. widget->log_handler = g_log_set_handler(NULL,G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,(GLogFunc) log_handler, widget); } LIB3270_EXPORT GtkWidget * v3270_trace_get_button_box(GtkWidget *widget) { g_return_val_if_fail(GTK_IS_V3270_TRACE(widget),NULL); return GTK_V3270_TRACE(widget)->buttons; } LIB3270_EXPORT void v3270_trace_button_box_insert(GtkWidget *widget, GtkWidget *button) { g_return_if_fail(GTK_IS_V3270_TRACE(widget)); gtk_widget_set_can_focus(button,FALSE); gtk_widget_set_can_default(button,FALSE); #if GTK_CHECK_VERSION(3,20,0) gtk_widget_set_focus_on_click(button,FALSE); #endif // GTK 3,20,0 gtk_box_pack_start(GTK_BOX(GTK_V3270_TRACE(widget)->buttons),button,FALSE,FALSE,0); } LIB3270_EXPORT GtkWidget * v3270_trace_new(GtkWidget *terminal) { g_return_val_if_fail(GTK_IS_V3270(terminal),NULL); V3270Trace * widget = GTK_V3270_TRACE(g_object_new(GTK_TYPE_V3270_TRACE, NULL)); // Set terminal widget { widget->terminal = terminal; g_object_ref_sink(G_OBJECT(terminal)); set_session(widget, v3270_get_session(widget->terminal)); } // Create toggle buttons { size_t ix; static const LIB3270_TOGGLE toggles[] = { LIB3270_TOGGLE_DS_TRACE, LIB3270_TOGGLE_NETWORK_TRACE, LIB3270_TOGGLE_EVENT_TRACE, LIB3270_TOGGLE_SSL_TRACE, LIB3270_TOGGLE_SCREEN_TRACE }; for(ix = 0; ix < G_N_ELEMENTS(toggles); ix++) { GtkWidget * item = v3270_toggle_button_new(widget->terminal,toggles[ix]); gtk_widget_set_can_focus(item,FALSE); gtk_widget_set_can_default(item,FALSE); #if GTK_CHECK_VERSION(3,20,0) gtk_widget_set_focus_on_click(item,FALSE); #endif // GTK 3,20,0 gtk_box_pack_start(GTK_BOX(widget->buttons),item,FALSE,FALSE,0); } } return GTK_WIDGET(widget); } struct _append_text { V3270Trace *widget; gchar text[1]; }; static gboolean bg_append_text(struct _append_text *cfg) { if(!GTK_IS_TEXT_BUFFER(cfg->widget->text)) return FALSE; GtkTextIter itr; gtk_text_buffer_get_end_iter(cfg->widget->text,&itr); if(g_utf8_validate(cfg->text,strlen(cfg->text),NULL)) { gtk_text_buffer_insert(cfg->widget->text,&itr,cfg->text,strlen(cfg->text)); } else { gtk_text_buffer_insert(cfg->widget->text,&itr,"** Invalid UTF8 String **",-1); } // gtk_text_buffer_get_end_iter(cfg->widget->text,&itr); // gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(cfg->widget->view), &itr, 0.0, FALSE, 0.0, 0.0); GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(cfg->widget->scroll); gtk_adjustment_set_value(vadj,gtk_adjustment_get_upper(vadj)); gtk_scrolled_window_set_vadjustment(cfg->widget->scroll, vadj); return FALSE; } LIB3270_EXPORT void v3270_trace_append_text(GtkWidget *widget, const gchar *text) { g_return_if_fail(GTK_IS_V3270_TRACE(widget)); // Enqueue update. struct _append_text * cfg = g_malloc0(sizeof(struct _append_text)+strlen(text)+1); cfg->widget = GTK_V3270_TRACE(widget); strcpy(cfg->text,text); g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,(GSourceFunc) bg_append_text, cfg, g_free); } LIB3270_EXPORT void v3270_trace_vprintf(GtkWidget *widget, const char *fmt, va_list args) { g_autofree gchar * text = g_strdup_vprintf(fmt,args); v3270_trace_append_text(widget,text); } LIB3270_EXPORT void v3270_trace_printf(GtkWidget *widget, const char *fmt, ... ) { va_list arg_ptr; va_start(arg_ptr, fmt); v3270_trace_vprintf(widget,fmt,arg_ptr); va_end(arg_ptr); } const gchar * v3270_trace_get_filename(GtkWidget *widget) { g_return_val_if_fail(GTK_IS_V3270_TRACE(widget),NULL); return GTK_V3270_TRACE(widget)->filename; } LIB3270_EXPORT void v3270_trace_save(GtkWidget *widget) { const gchar *filename = v3270_trace_get_filename(widget); V3270Trace * trace = GTK_V3270_TRACE(widget); if(filename) { GError * error = NULL; gchar * text; GtkTextIter start; GtkTextIter end; gtk_text_buffer_get_start_iter(trace->text,&start); gtk_text_buffer_get_end_iter(trace->text,&end); text = gtk_text_buffer_get_text(trace->text,&start,&end,FALSE); g_file_set_contents(trace->filename,text,-1,&error); g_free(text); if(error) { v3270_popup_gerror( widget, error, NULL, _( "Can't save %s" ), filename ); g_error_free(error); } } } LIB3270_EXPORT void v3270_trace_select_file(GtkWidget *widget) { V3270Trace * trace = GTK_V3270_TRACE(widget); gchar * filename = v3270_select_file( GTK_WIDGET(trace), _("Save trace to file"), _("Save"), GTK_FILE_CHOOSER_ACTION_SAVE, trace->filename, N_("Text file"), "*.txt", NULL ); if(filename) { g_free(trace->filename); trace->filename = filename; v3270_trace_save(widget); } }