Qt: Add Lua console dialog

This adds a dialog in the Tools menu to open a console and evaluate
Lua code using the embedded Lua engine. It replaces the previous
console.lua implementation that was more limited to use, because
it relies on GUI bits exposed to Lua. It used two separate windows
for that reason.

The implementation uses the existing "funnel" API amd  relies heavily
on callbacks to maintain separation between GUI and epan code and
make it generic enough to possibly support more use cases than just
the Lua 5.2 console.

The open and close callbacks are used to install and remove a custom
print() lua function with dialog creation and destruction.

The eval callback is basically the same as luaL_dostring().
This commit is contained in:
João Valverde 2023-08-13 19:48:59 +01:00
parent de1d30cb4e
commit 8ed0b47667
14 changed files with 648 additions and 9 deletions

View File

@ -164,6 +164,8 @@ can be achieved via other fields, prefer that.
* The Windows build has a new SpeexDSP external dependency (https://www.speex.org).
The speex code that was previously bundled has been removed.
* The Lua console dialogs under Tools were refactored and redesigned. It now
consists of a single dialog window for input and output (Lua 5.2 only).
=== Removed Features and Support

View File

@ -765,10 +765,8 @@ It is assumed that the rules will be applied to an outside interface.
Menu item is greyed out unless one (and only one) frame is selected in the packet list.
|menu:Credentials[]|| This allows you to extract credentials from the current capture file. Some of the dissectors (ftp, http, imap, pop, smtp) have been instrumented to provide the module with usernames and passwords and more will be instrumented in the future. The window dialog provides you the packet number where the credentials have been found, the protocol that provided them, the username and protocol specific information.
|menu:MAC Address Blocks[]|| This allows viewing the IEEE MAC address registry data that Wireshark uses to resolve MAC address blocks to vendor names. The table can be searched by address prefix or vendor name.
|menu:Lua[]|| These options allow you to work with the Lua interpreter optionally built into Wireshark.
|menu:Lua Console[]|| This option allows you to work with the Lua interpreter optionally built into Wireshark, to inspect Lua internals and evaluate code.
See “Lua Support in Wireshark” in the Wireshark Developers Guide.
The Lua menu structure is set by console.lua in the Wireshark install directory.
|===
[#ChUseHelpMenuSection]

View File

@ -27,6 +27,15 @@ typedef struct _funnel_menu_t {
struct _funnel_menu_t* next;
} funnel_menu_t;
typedef struct _console_menu {
char *name;
funnel_console_eval_cb_t eval_cb;
funnel_console_open_cb_t open_cb;
funnel_console_close_cb_t close_cb;
void *user_data;
funnel_console_data_free_cb_t data_free_cb;
} funnel_console_menu_t;
/* XXX This assumes one main window and one capture file. */
static const funnel_ops_t* ops = NULL;
static funnel_menu_t* registered_menus = NULL;
@ -58,6 +67,9 @@ typedef struct _funnel_packet_menu_t {
* List of all registered funnel_packet_menu_t's
*/
static funnel_packet_menu_t* registered_packet_menus = NULL;
static GSList *registered_console_menus = NULL;
/*
* TRUE if the packet menus were modified since the last registration
*/
@ -67,6 +79,8 @@ static void funnel_clear_packet_menu (funnel_packet_menu_t** menu_list);
const funnel_ops_t* funnel_get_funnel_ops(void) { return ops; }
void funnel_set_funnel_ops(const funnel_ops_t* o) { ops = o; }
static void funnel_clear_console_menu(void);
static void funnel_insert_menu (funnel_menu_t** menu_list, funnel_menu_t *menu)
{
if (!(*menu_list)) {
@ -277,6 +291,50 @@ void funnel_cleanup(void)
{
funnel_clear_menu(&registered_menus);
funnel_clear_packet_menu(&registered_packet_menus);
funnel_clear_console_menu();
}
/**
* Entry point for code to register a console menu
*/
void funnel_register_console_menu(const char *name,
funnel_console_eval_cb_t eval_cb,
funnel_console_open_cb_t open_cb,
funnel_console_close_cb_t close_cb,
gpointer callback_data,
funnel_console_data_free_cb_t free_data)
{
funnel_console_menu_t* m = g_new0(funnel_console_menu_t, 1);
m->name = g_strdup(name);
m->eval_cb = eval_cb;
m->open_cb = open_cb;
m->close_cb = close_cb;
m->user_data = callback_data;
m->data_free_cb = free_data;
registered_console_menus = g_slist_prepend(registered_console_menus, m);
}
void funnel_register_all_console_menus(funnel_registration_console_cb_t r_cb)
{
GSList *l;
for (l = registered_console_menus; l != NULL; l = l->next) {
funnel_console_menu_t *m = l->data;
r_cb(m->name, m->eval_cb, m->open_cb, m->close_cb, m->user_data);
}
}
static void funnel_clear_console_menu(void)
{
GSList *l;
for (l = registered_console_menus; l != NULL; l = l->next) {
funnel_console_menu_t *m = l->data;
if (m->data_free_cb && m->user_data) {
m->data_free_cb(m->user_data);
}
}
g_slist_free(registered_console_menus);
registered_console_menus = NULL;
}
/*

View File

@ -13,10 +13,8 @@
#ifndef __FUNNEL_H__
#define __FUNNEL_H__
#include <glib.h>
#include <wireshark.h>
#include <epan/stat_groups.h>
#include "ws_symbol_export.h"
#include <ws_log_defs.h>
#ifdef __cplusplus
extern "C" {
@ -161,6 +159,66 @@ WS_DLL_PUBLIC void funnel_register_packet_menu(const char *name,
*/
WS_DLL_PUBLIC gboolean funnel_packet_menus_modified(void);
/*
* The functions below allow registering a funnel "console". A console is just a GUI
* dialog that has an input text widget, an output text widget, and for each user
* generated input it calls a callback to generate the corresponding output.
* Very simple... each console type has a name and an entry in the Tools menu to invoke it.
* Mainly used to present a Lua console to allow inspecting Lua internals and run Lua
* code using the embedded interpreter.
*/
/**
* Signature of function that can be called to evaluate code.
* Returns zero on success, -1 if precompilation failed, positive for runtime errors.
*/
typedef int (*funnel_console_eval_cb_t)(const char *console_input,
char **error_ptr,
char **error_hint,
void *callback_data);
/**
* Signature of function that can be called to install a logger. Returns opaque
* pinter to restore original logger.
*/
typedef intptr_t (*funnel_console_open_cb_t)(void (*print_func)(const char *, void *), void *print_data, void *callback_data);
/**
* Signature of function that can be called to remove logger.
*/
typedef void (*funnel_console_close_cb_t)(intptr_t blob, void *callback_data);
/**
* Signature of function that can be called to free user data.
*/
typedef void (*funnel_console_data_free_cb_t)(void *callback_data);
/**
* Entry point for Lua code to register a console menu
*/
WS_DLL_PUBLIC void funnel_register_console_menu(const char *name,
funnel_console_eval_cb_t eval_cb,
funnel_console_open_cb_t open_cb,
funnel_console_close_cb_t close_cb,
void *callback_data,
funnel_console_data_free_cb_t free_data);
/**
* Signature of callback function to register console menu entries
*/
typedef void (*funnel_registration_console_cb_t)(const char *name,
funnel_console_eval_cb_t eval_cb,
funnel_console_open_cb_t open_cb,
funnel_console_close_cb_t close_cb,
void *callback_data);
/**
* Entry point for Wireshark GUI to obtain all registered console menus
*
* @param r_cb function which will be called to register each console menu entry
*/
WS_DLL_PUBLIC void funnel_register_all_console_menus(funnel_registration_console_cb_t r_cb);
extern void initialize_funnel_ops(void);
extern void funnel_dump_all_text_windows(void);

View File

@ -123,6 +123,9 @@ persconffile_path = Dir.personal_config_path
if not running_superuser or run_user_scripts_when_superuser then
dofile(DATA_DIR.."console.lua")
if _VERSION == "Lua 5.1" then
-- This has been deprecated and replaced with Lua 5.2.
dofile(DATA_DIR.."console.lua")
end
dofile(DATA_DIR.."browser_sslkeylog.lua")
end

View File

@ -13,6 +13,7 @@
*/
#include "config.h"
#define WS_LOG_DOMAIN LOG_DOMAIN_WSLUA
#include "wslua.h"
#include "init_wslua.h"
@ -1122,6 +1123,120 @@ wslua_add_introspection(void)
}
}
#if LUA_VERSION_NUM >= 502
static const char *lua_error_msg(int code)
{
switch (code) {
case LUA_ERRSYNTAX: return "syntax error during precompilation";
case LUA_ERRMEM: return "memory allocation error";
case LUA_ERRGCMM: return "error while running a __gc metamethod";
case LUA_ERRRUN: return "runtime error";
case LUA_ERRERR: return "error while running the message handler";
default: break; /* Should not happen. */
}
return "unknown error";
}
static int lua_funnel_console_eval(const char *console_input,
char **error_ptr,
char **error_hint,
void *callback_data)
{
ws_noisy("Console input: %s", console_input);
lua_State *_L = callback_data;
int lcode;
lcode = luaL_loadstring(_L, console_input);
if (lcode != LUA_OK) {
ws_debug("luaL_loadstring(): %s (%d)", lua_error_msg(lcode), lcode);
if (error_hint) {
*error_hint = g_strdup(lua_error_msg(lcode));
}
return -1;
}
lcode = lua_pcall(_L, 0, LUA_MULTRET, 0);
if (lcode != LUA_OK) {
ws_debug("lua_pcall(): %s (%d)", lua_error_msg(lcode), lcode);
if (error_hint) {
*error_hint = g_strdup(lua_error_msg(lcode));
}
/* If we have an error message return it. */
if (error_ptr && !lua_isnil(_L, -1)) {
*error_ptr = g_strdup(lua_tostring(_L, -1));
}
return 1;
}
ws_noisy("Success");
return 0;
}
/* Receives C print function pointer as first upvalue. */
/* Receives C print function data pointer as second upvalue. */
static int wslua_console_print(lua_State *_L)
{
void (*gui_print_func)(const char *, void *) = lua_touserdata(_L, lua_upvalueindex(1));
void *gui_print_ptr = lua_touserdata(_L, lua_upvalueindex(2));
GString *gstr = g_string_new(NULL);
const char *repr;
/* Print arguments. */
for (int i = 1; i <= lua_gettop(_L); i++) {
repr = luaL_tolstring(_L, i, NULL);
if (i > 1)
g_string_append_c(gstr, '\t');
g_string_append(gstr, repr);
lua_pop(_L, 1);
}
g_string_append_c(gstr, '\n');
gui_print_func(gstr->str, gui_print_ptr);
g_string_free(gstr, TRUE);
return 0;
}
// Replace lua print function with a custom print function.
// We will place the original function in the Lua registry and return the reference.
static intptr_t lua_funnel_console_open(void (*gui_print_func)(const char *, void *),
void *gui_print_ptr,
void *callback_data)
{
lua_State *_L = callback_data;
intptr_t ref;
/* Store original print value in the registry (even if it is nil). */
lua_getglobal(_L, "print");
ref = luaL_ref(_L, LUA_REGISTRYINDEX);
/* Set new "print" function (to output to the GUI) */
/* Push upvalues */
lua_pushlightuserdata(_L, gui_print_func);
lua_pushlightuserdata(_L, gui_print_ptr);
// Push closure
lua_pushcclosure(_L, wslua_console_print, 2);
lua_setglobal(_L, "print");
/* return original print function registry reference. */
return ref;
}
// Restore original Lua print function. Clean state.
static void lua_funnel_console_close(intptr_t wslua_print_ref,
void *callback_data)
{
lua_State *_L = callback_data;
/* Restore the original print function. */
int ref = (int)wslua_print_ref;
/* push original function into stack */
lua_rawgeti(_L, LUA_REGISTRYINDEX, ref);
lua_setglobal(_L, "print");
/* Release reference */
luaL_unref(_L, LUA_REGISTRYINDEX, ref);
}
#endif /* LUA_VERSION_NUM >= 502 */
void wslua_init(register_cb cb, gpointer client_data) {
gchar* filename;
const funnel_ops_t* ops = funnel_get_funnel_ops();
@ -1321,6 +1436,15 @@ void wslua_init(register_cb cb, gpointer client_data) {
wslua_add_introspection();
#if LUA_VERSION_NUM >= 502
// Register Lua's console menu (in the GUI)
funnel_register_console_menu("Lua",
lua_funnel_console_eval,
lua_funnel_console_open,
lua_funnel_console_close,
L, NULL);
#endif /* LUA_VERSION_NUM >= 502 */
/* load system's init.lua */
filename = get_datafile_path("init.lua");
if (( file_exists(filename))) {

View File

@ -50,6 +50,8 @@
#define LOG_DOMAIN_PLUGINS "Plugins"
#define LOG_DOMAIN_WSLUA "WSLua"
/*
* Ascending order by priority needs to be maintained. Higher priorities have
* higher values.

View File

@ -180,6 +180,7 @@ set(WIRESHARK_QT_HEADERS
../qt/interface_frame.h
../qt/interface_toolbar_reader.h
../qt/interface_toolbar.h
../qt/io_console_dialog.h
../qt/io_graph_dialog.h
../qt/layout_preferences_frame.h
../qt/main_application.h
@ -400,6 +401,7 @@ set(WIRESHARK_QT_SRC
../qt/interface_frame.cpp
../qt/interface_toolbar_reader.cpp
../qt/interface_toolbar.cpp
../qt/io_console_dialog.cpp
../qt/layout_preferences_frame.cpp
../qt/main_application.cpp
../qt/main_status_bar.cpp
@ -511,6 +513,7 @@ set(WIRESHARK_QT_UI
../qt/import_text_dialog.ui
../qt/interface_frame.ui
../qt/interface_toolbar.ui
../qt/io_console_dialog.ui
../qt/io_graph_dialog.ui
../qt/layout_preferences_frame.ui
../qt/main_window_preferences_frame.ui

View File

@ -188,6 +188,7 @@ set(WIRESHARK_QT_HEADERS
interface_frame.h
interface_toolbar_reader.h
interface_toolbar.h
io_console_dialog.h
io_graph_dialog.h
layout_preferences_frame.h
lbm_lbtrm_transport_dialog.h
@ -436,6 +437,7 @@ set(WIRESHARK_QT_SRC
interface_frame.cpp
interface_toolbar_reader.cpp
interface_toolbar.cpp
io_console_dialog.cpp
layout_preferences_frame.cpp
lbm_lbtrm_transport_dialog.cpp
lbm_lbtru_transport_dialog.cpp
@ -585,6 +587,7 @@ set(WIRESHARK_QT_UI
import_text_dialog.ui
interface_frame.ui
interface_toolbar.ui
io_console_dialog.ui
io_graph_dialog.ui
layout_preferences_frame.ui
lbm_lbtrm_transport_dialog.ui

View File

@ -22,6 +22,7 @@
#include "ui/progress_dlg.h"
#include "ui/simple_dialog.h"
#include <ui/qt/main_window.h>
#include <ui/qt/io_console_dialog.h>
#include "funnel_statistics.h"
#include "funnel_string_dialog.h"
@ -63,6 +64,12 @@ static void progress_window_update(struct progdlg *progress_dialog, float percen
static void progress_window_destroy(struct progdlg *progress_dialog);
}
FunnelAction::FunnelAction(QObject *parent) :
QAction(parent)
{
}
FunnelAction::FunnelAction(QString title, funnel_menu_callback callback, gpointer callback_data, gboolean retap, QObject *parent = nullptr) :
QAction(parent),
title_(title),
@ -180,7 +187,6 @@ void FunnelAction::addToMenu(QMenu * ctx_menu, QHash<QString, QMenu *> menuTextT
}
void FunnelAction::triggerPacketCallback() {
if (packetCallback_) {
packetCallback_(callback_data_, packetData_);
@ -196,6 +202,35 @@ QString FunnelAction::getPacketSubmenus() {
return packetSubmenu_;
}
FunnelConsoleAction::FunnelConsoleAction(QString name,
funnel_console_eval_cb_t eval_cb,
funnel_console_open_cb_t open_cb,
funnel_console_close_cb_t close_cb,
void *callback_data, QObject *parent = nullptr) :
FunnelAction(parent),
eval_cb_(eval_cb),
open_cb_(open_cb),
close_cb_(close_cb),
callback_data_(callback_data)
{
// Use "&&" to get a real ampersand in the menu item.
QString title = QString("%1 Console").arg(name).replace('&', "&&");
setText(title);
setObjectName(FunnelStatistics::actionName());
}
FunnelConsoleAction::~FunnelConsoleAction()
{
}
void FunnelConsoleAction::triggerCallback() {
IOConsoleDialog *dialog = new IOConsoleDialog(*qobject_cast<QWidget *>(parent()),
this->text(),
eval_cb_, open_cb_, close_cb_, callback_data_);
dialog->show();
}
static QHash<int, QList<FunnelAction *> > funnel_actions_;
const QString FunnelStatistics::action_name_ = "FunnelStatisticsAction";
static gboolean menus_registered = FALSE;
@ -509,6 +544,7 @@ void
register_tap_listener_qt_funnel(void)
{
funnel_register_all_menus(register_menu_cb);
funnel_statistics_load_console_menus();
menus_registered = TRUE;
}
@ -541,4 +577,36 @@ funnel_statistics_load_packet_menus(void)
funnel_register_all_packet_menus(register_packet_menu_cb);
}
static void register_console_menu_cb(const char *name,
funnel_console_eval_cb_t eval_cb,
funnel_console_open_cb_t open_cb,
funnel_console_close_cb_t close_cb,
void *callback_data)
{
FunnelConsoleAction *funnel_action = new FunnelConsoleAction(name, eval_cb,
open_cb,
close_cb,
callback_data,
mainApp);
if (menus_registered) {
mainApp->appendDynamicMenuGroupItem(REGISTER_TOOLS_GROUP_UNSORTED, funnel_action);
} else {
mainApp->addDynamicMenuGroupItem(REGISTER_TOOLS_GROUP_UNSORTED, funnel_action);
}
if (!funnel_actions_.contains(REGISTER_TOOLS_GROUP_UNSORTED)) {
funnel_actions_[REGISTER_TOOLS_GROUP_UNSORTED] = QList<FunnelAction *>();
}
funnel_actions_[REGISTER_TOOLS_GROUP_UNSORTED] << funnel_action;
}
/*
* Loads all registered console menus into the
* Wireshark GUI.
*/
void
funnel_statistics_load_console_menus(void)
{
funnel_register_all_console_menus(register_console_menu_cb);
}
} // extern "C"

View File

@ -65,12 +65,13 @@ class FunnelAction : public QAction
{
Q_OBJECT
public:
FunnelAction(QObject *parent = nullptr);
FunnelAction(QString title, funnel_menu_callback callback, gpointer callback_data, gboolean retap, QObject *parent);
FunnelAction(QString title, funnel_packet_menu_callback callback, gpointer callback_data, gboolean retap, const char *packet_required_fields, QObject *parent);
~FunnelAction();
funnel_menu_callback callback() const;
QString title() const;
void triggerCallback();
virtual void triggerCallback();
void setPacketCallback(funnel_packet_menu_callback packet_callback);
void setPacketData(GPtrArray* finfos);
void addToMenu(QMenu * ctx_menu, QHash<QString, QMenu *> menuTextToMenus);
@ -93,9 +94,29 @@ private:
QSet<QString> packetRequiredFields_;
};
class FunnelConsoleAction : public FunnelAction
{
Q_OBJECT
public:
FunnelConsoleAction(QString name, funnel_console_eval_cb_t eval_cb,
funnel_console_open_cb_t open_cb,
funnel_console_close_cb_t close_cb,
void *callback_data, QObject *parent);
~FunnelConsoleAction();
virtual void triggerCallback();
private:
QString title_;
funnel_console_eval_cb_t eval_cb_;
funnel_console_open_cb_t open_cb_;
funnel_console_close_cb_t close_cb_;
void *callback_data_;
};
extern "C" {
void funnel_statistics_reload_menus(void);
void funnel_statistics_load_packet_menus(void);
void funnel_statistics_load_console_menus(void);
gboolean funnel_statistics_packet_menus_modified(void);
} // extern "C"

128
ui/qt/io_console_dialog.cpp Normal file
View File

@ -0,0 +1,128 @@
/*
* io_console_dialog.c
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config.h>
#define WS_LOG_DOMAIN LOG_DOMAIN_QTUI
#include <QPlainTextEdit>
#include <QKeySequence>
#include <QPushButton>
#include "io_console_dialog.h"
#include <ui_io_console_dialog.h>
#include "main_application.h"
extern "C" {
static void print_function(const char *str, void *ptr);
}
static void print_function(const char *str, void *print_data)
{
IOConsoleDialog *dialog = static_cast<IOConsoleDialog *>(print_data);
dialog->appendOutputText(QString(str));
}
IOConsoleDialog::IOConsoleDialog(QWidget &parent,
QString title,
funnel_console_eval_cb_t eval_cb,
funnel_console_open_cb_t open_cb,
funnel_console_close_cb_t close_cb,
void *callback_data = nullptr) :
GeometryStateDialog(&parent),
ui(new Ui::IOConsoleDialog),
eval_cb_(eval_cb),
open_cb_(open_cb),
close_cb_(close_cb),
callback_data_(callback_data)
{
ui->setupUi(this);
if (title.isEmpty())
title = QString("Console");
loadGeometry(0, 0, title);
setWindowTitle(mainApp->windowTitleString(title));
QPushButton *eval_button = ui->buttonBox->addButton(tr("Evaluate"), QDialogButtonBox::ActionRole);
eval_button->setDefault(true);
eval_button->setShortcut(QKeySequence("Ctrl+Return"));
connect(eval_button, &QPushButton::clicked, this, &IOConsoleDialog::acceptInput);
QPushButton *clear_button = ui->buttonBox->addButton(tr("Clear"), QDialogButtonBox::ActionRole);
connect(clear_button, &QPushButton::clicked, this, &IOConsoleDialog::on_clearActivated);
ui->inputPlainTextEdit->setFont(mainApp->monospaceFont());
ui->outputPlainTextEdit->setFont(mainApp->monospaceFont());
ui->hintLabel->clear();
// Install print and save return
saved_blob_ = open_cb_(print_function, this, callback_data_);
}
IOConsoleDialog::~IOConsoleDialog()
{
delete ui;
close_cb_(saved_blob_, callback_data_);
}
void IOConsoleDialog::setHintText(const QString &text)
{
ui->hintLabel->setText(QString("<small><i>%1.</i></small>").arg(text));
}
void IOConsoleDialog::clearHintText()
{
ui->hintLabel->clear();
}
void IOConsoleDialog::acceptInput()
{
clearHintText();
QString text = ui->inputPlainTextEdit->toPlainText();
if (text.isEmpty())
return;
char *error_str = nullptr;
char *error_hint = nullptr;
int result = eval_cb_(qUtf8Printable(text), &error_str, &error_hint, callback_data_);
if (result != 0) {
if (error_hint) {
QString hint(error_hint);
setHintText(hint.at(0).toUpper() + hint.mid(1));
g_free(error_hint);
}
else if (result < 0) {
setHintText("Error loading string");
}
else {
setHintText("Error running chunk");
}
if (error_str) {
appendOutputText(QString(error_str));
g_free(error_str);
}
}
}
void IOConsoleDialog::appendOutputText(const QString &text)
{
ui->outputPlainTextEdit->appendPlainText(text);
}
void IOConsoleDialog::on_clearActivated()
{
ui->inputPlainTextEdit->clear();
ui->outputPlainTextEdit->clear();
ui->hintLabel->clear();
}

53
ui/qt/io_console_dialog.h Normal file
View File

@ -0,0 +1,53 @@
/** @file
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef IO_CONSOLE_DIALOG_H
#define IO_CONSOLE_DIALOG_H
#include <wireshark.h>
#include <QDialog>
#include "geometry_state_dialog.h"
#include <epan/funnel.h>
namespace Ui {
class IOConsoleDialog;
}
class IOConsoleDialog : public GeometryStateDialog
{
Q_OBJECT
public:
explicit IOConsoleDialog(QWidget &parent,
QString title,
funnel_console_eval_cb_t eval_cb,
funnel_console_open_cb_t open_cb,
funnel_console_close_cb_t close_cb,
void *callback_data);
~IOConsoleDialog();
void appendOutputText(const QString &text);
void setHintText(const QString &text);
void clearHintText();
private slots:
void acceptInput();
void on_clearActivated(void);
private:
Ui::IOConsoleDialog *ui;
funnel_console_eval_cb_t eval_cb_;
funnel_console_open_cb_t open_cb_;
funnel_console_close_cb_t close_cb_;
void *callback_data_;
intptr_t saved_blob_;
};
#endif // IO_CONSOLE_DIALOG_H

118
ui/qt/io_console_dialog.ui Normal file
View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>IOConsoleDialog</class>
<widget class="QDialog" name="IOConsoleDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>596</width>
<height>430</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Input</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="inputPlainTextEdit">
<property name="placeholderText">
<string>Use Ctrl+Enter to evaluate code.</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Output</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="outputPlainTextEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="hintLabel">
<property name="text">
<string notr="true">&lt;small&gt;&lt;i&gt;A hint.&lt;/i&gt;&lt;/small&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>IOConsoleDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>IOConsoleDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>