#include "xincludes.h"

#include <librnd/rnd_config.h>
#include <librnd/core/math_helper.h>
#include <librnd/core/rnd_conf.h>
#include <librnd/core/hidlib.h>
#include <librnd/hid/pixmap.h>

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <signal.h>
#include <setjmp.h>

#include <librnd/core/color.h>
#include <librnd/core/color_cache.h>
#include <librnd/core/conf_hid.h>
#include <librnd/core/rnd_printf.h>
#include <librnd/core/error.h>
#include <librnd/core/event.h>
#include <librnd/core/plugins.h>
#include <librnd/core/safe_fs.h>

#include <librnd/hid/hid.h>
#include <librnd/hid/hid_nogui.h>
#include <librnd/core/hid_cfg.h>
#include "lesstif.h"
#include <librnd/hid/hid_cfg_input.h>
#include <librnd/hid/hid_attrib.h>
#include <librnd/hid/hid_init.h>
#include <librnd/hid/hid_dad.h>
#include <librnd/core/actions.h>
#include "ltf_stdarg.h"
#include <librnd/hid/grid.h>
#include <librnd/core/misc_util.h>
#include <librnd/core/compat_misc.h>
#include <librnd/hid/tool.h>
#include <librnd/core/globalconst.h>

#include "wt_preview.h"
#include "dialogs.h"
#include "render.h"

#include "FillBox.h"

#include <librnd/plugins/lib_hid_common/lib_hid_common.h>
#include <librnd/plugins/lib_hid_common/clip.h>
#include <librnd/plugins/lib_hid_common/cli_history.h>

#include <sys/poll.h>

const char *lesstif_cookie = "lesstif HID";

rnd_hid_cfg_mouse_t lesstif_mouse;
rnd_hid_cfg_keys_t lesstif_keymap;
int lesstif_active = 0;

static int idle_proc_set = 0;

#ifndef XtRDouble
#define XtRDouble "Double"
#endif

rnd_hid_t lesstif_hid;

#define CRASH(func) fprintf(stderr, "HID error: app called unimplemented GUI function %s\n", func), abort()

XtAppContext app_context;
Widget appwidget, ltf_fullscreen_left, ltf_fullscreen_top, ltf_fullscreen_bottom;
Display *display;

Screen *screen_s;
int screen;

static int in_move_event = 0;

Widget mainwind;
Widget work_area, messages, command, hscroll, vscroll;
Widget m_click;

static void lesstif_reg_attrs(void);
static void lesstif_begin(void);
static void lesstif_end(void);

static Widget ltf_dockbox[RND_HID_DOCK_max];
static gdl_list_t ltf_dock[RND_HID_DOCK_max];

typedef struct {
	void *hid_ctx;
	Widget hvbox;
	rnd_hid_dock_t where;
} docked_t;

static Widget ltf_create_dockbox(Widget parent, rnd_hid_dock_t where, int vert)
{
	stdarg(PxmNfillBoxVertical, vert);
	stdarg(XmNmarginWidth, 0);
	stdarg(XmNmarginHeight, 0);
	ltf_dockbox[where] = PxmCreateFillBox(parent, "dockbox", stdarg_args, stdarg_n);

	return ltf_dockbox[where];
}

static int ltf_dock_poke(rnd_hid_dad_subdialog_t *sub, const char *cmd, rnd_event_arg_t *res, int argc, rnd_event_arg_t *argv)
{
	return -1;
}

static htsp_t pck_dock_pos[RND_HID_DOCK_max];

static void lft_dock_init(void)
{
	int n;
	for(n = 0; n < RND_HID_DOCK_max; n++)
		htsp_init(&pck_dock_pos[n], strhash, strkeyeq);
}

void lft_dock_uninit(void)
{
	int n;
	for(n = 0; n < RND_HID_DOCK_max; n++) {
		htsp_entry_t *e;
		for(e = htsp_first(&pck_dock_pos[n]); e != NULL; e = htsp_next(&pck_dock_pos[n], e))
			free(e->key);
		htsp_uninit(&pck_dock_pos[n]);
	}
}

static int ltf_dock_enter(rnd_hid_t *hid, rnd_hid_dad_subdialog_t *sub, rnd_hid_dock_t where, const char *id)
{
	docked_t *docked;
	Widget frame;
	int expfill = 0;

	if (ltf_dockbox[where] == NULL)
		return -1;

	docked = calloc(sizeof(docked_t), 1);
	docked->where = where;

	if (RND_HATT_IS_COMPOSITE(sub->dlg[0].type))
		expfill = (sub->dlg[0].rnd_hatt_flags & RND_HATF_EXPFILL);

	frame = htsp_get(&pck_dock_pos[where], id);
	if (frame == NULL) {
		if (rnd_dock_has_frame[where]) {
			stdarg_n = 0;
			stdarg(XmNalignment, XmALIGNMENT_END);
			stdarg(XmNmarginWidth, 0);
			stdarg(XmNmarginHeight, 0);
			stdarg(PxmNfillBoxFill, expfill);
			frame = XmCreateFrame(ltf_dockbox[where], XmStrCast(id), stdarg_args, stdarg_n);
		}
		else {
			stdarg_n = 0;
			stdarg(PxmNfillBoxVertical, 0);
			stdarg(XmNmarginWidth, 0);
			stdarg(XmNmarginHeight, 0);
			stdarg(PxmNfillBoxFill, expfill);
			frame = PxmCreateFillBox(ltf_dockbox[where], XmStrCast(id), stdarg_args, stdarg_n);
		}
		htsp_set(&pck_dock_pos[where], rnd_strdup(id), frame);
	}

	XtManageChild(frame);

	stdarg_n = 0;
	stdarg(PxmNfillBoxVertical, rnd_dock_is_vert[where]);
	stdarg(XmNmarginWidth, 0);
	stdarg(XmNmarginHeight, 0);
	stdarg(PxmNfillBoxFill, expfill);
	docked->hvbox = PxmCreateFillBox(frame, "dockbox", stdarg_args, stdarg_n);
	XtManageChild(docked->hvbox);

	sub->parent_poke = ltf_dock_poke;
	sub->dlg_hid_ctx = docked->hid_ctx = lesstif_attr_sub_new(docked->hvbox, sub->dlg, sub->dlg_len, sub);
	sub->parent_ctx = docked;

	gdl_append(&ltf_dock[where], sub, link);

	return 0;
}

static void ltf_dock_leave(rnd_hid_t *hid, rnd_hid_dad_subdialog_t *sub)
{
	docked_t *docked = sub->parent_ctx;
	Widget frame = XtParent(docked->hvbox);

	XtDestroyWidget(docked->hvbox);

	gdl_remove(&ltf_dock[docked->where], sub, link);
	free(docked);
	RND_DAD_FREE(sub->dlg);

	XtUnmanageChild(frame);
}

static void ltf_update_topwin_dock_hidlibs(rnd_design_t *new_dsg)
{
	int n;
	for(n = 0; n < RND_HID_DOCK_max; n++) {
		rnd_hid_dad_subdialog_t *sub;
		for(sub = gdl_first(&ltf_dock[n]); sub != NULL; sub = gdl_next(&ltf_dock[n], sub)) {
			docked_t *docked = sub->parent_ctx;
			lesstif_attr_sub_update_hidlib(docked->hid_ctx, new_dsg);
		}
	}
}

typedef struct {
	void *hid_ctx;
	Widget frame;
	rnd_hid_dock_t where;
} ltf_docked_t;

static void ShowCrosshair(rnd_bool show)
{
	if (xrnd.crosshair_on == show)
		return;

	rnd_hid_notify_crosshair_change(xrnd.dsg, rnd_false);
	xrnd_notify_mark_change(&lesstif_hid, rnd_false);

	xrnd.crosshair_on = show;

	rnd_hid_notify_crosshair_change(xrnd.dsg, rnd_true);
	xrnd_notify_mark_change(&lesstif_hid, rnd_true);
}


const rnd_export_opt_t lesstif_attribute_list[] = {
	{"install", "Install private colormap",
		RND_HATT_BOOL, 0, 0, {0, 0, 0}, 0},
#define HA_colormap 0

/* %start-doc options "22 lesstif GUI Options"
@ftable @code
@item --listen
Listen for actions on stdin.
@end ftable
%end-doc
*/
	{"listen", "Listen on standard input for actions",
		RND_HATT_BOOL, 0, 0, {0, 0, 0}, 0},
#define HA_listen 1

/* %start-doc options "22 lesstif GUI Options"
@ftable @code
@item --bg-image <string>
File name of an image to put into the background of the GUI canvas. The image must
be a color PPM image, in binary (not ASCII) format. It can be any size, and will be
automatically scaled to fit the canvas.
@end ftable
%end-doc
*/
	{"bg-image", "Background Image",
		RND_HATT_STRING, 0, 0, {0, 0, 0}, 0},
#define HA_bg_image 2
};


#define NUM_OPTIONS ((sizeof(lesstif_attribute_list) / sizeof(lesstif_attribute_list[0])))

rnd_hid_attr_val_t ltf_values[NUM_OPTIONS];

/* ---------------------------------------------------------------------- */

/* Local actions.  */

extern void LesstifNetlistChanged(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[]);
extern void LesstifLibraryChanged(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[]);


static void ltf_set_hidlib(rnd_hid_t *hid, rnd_design_t *hidlib)
{
	rnd_coord_t siz;
	xrnd.dsg = hidlib;
	ltf_update_topwin_dock_hidlibs(hidlib);
	if ((work_area == 0) || (hidlib == NULL))
		return;
	/*rnd_printf("PCB Changed! %$mD\n", rnd_dwg_get_size_x(xrnd.dsg), rnd_dwg_get_size_y(xrnd.dsg)); */

	if (!rnd_conf.editor.unlimited_pan) {
		siz = rnd_dwg_get_size_x(xrnd.dsg);
		stdarg_n = 0;
		stdarg(XmNminimum, xrnd.dsg->dwg.X1);
		stdarg(XmNvalue, xrnd.dsg->dwg.X1);
		stdarg(XmNsliderSize, siz > 0 ? siz : 1);
		stdarg(XmNmaximum, xrnd.dsg->dwg.X2 ? xrnd.dsg->dwg.X2 : 1);
		XtSetValues(hscroll, stdarg_args, stdarg_n);
	}

	if (!rnd_conf.editor.unlimited_pan) {
		siz = rnd_dwg_get_size_y(xrnd.dsg);
		stdarg_n = 0;
		stdarg(XmNminimum, xrnd.dsg->dwg.Y1);
		stdarg(XmNvalue, xrnd.dsg->dwg.Y1);
		stdarg(XmNsliderSize, siz > 0 ? siz : 1);
		stdarg(XmNmaximum, xrnd.dsg->dwg.Y2 ? xrnd.dsg->dwg.Y2 : 1);
		XtSetValues(vscroll, stdarg_args, stdarg_n);
	}

	xrnd_zoom_max();

	lesstif_update_layer_groups();
	return;
}

static Widget m_cmd = 0, m_cmd_label;
static int cmd_is_active = 0;

static void command_hide(void)
{
	XtUnmanageChild(m_cmd);
	XtUnmanageChild(m_cmd_label);
	if (rnd_conf.editor.fullscreen)
		XtUnmanageChild(ltf_fullscreen_bottom);
	XmProcessTraversal(work_area, XmTRAVERSE_CURRENT);
	cmd_is_active = 0;
}

static void command_callback(Widget w, XtPointer uptr, XmTextVerifyCallbackStruct * cbs)
{
	char *s;
	switch (cbs->reason) {
	case XmCR_ACTIVATE:
		s = XmTextGetString(w);
		xrnd_show_crosshair(0);
		rnd_clihist_append(s, NULL, NULL, NULL);
		rnd_parse_command(xrnd.dsg, s, rnd_false);
		XtFree(s);
		XmTextSetString(w, XmStrCast(""));

		command_hide();

		break;
	}
}

static int shift_pressed;
static int ctrl_pressed;
static int alt_pressed;

static void ltf_mod_key(XKeyEvent *e, int set, int mainloop)
{
	switch (XKeycodeToKeysym(display, e->keycode, 0)) {
	case XK_Shift_L:
	case XK_Shift_R:
		shift_pressed = set;
		break;
	case XK_Control_L:
	case XK_Control_R:
		ctrl_pressed = set;
		break;
#ifdef __APPLE__
	case XK_Mode_switch:
#else
	case XK_Alt_L:
	case XK_Alt_R:
#endif
		alt_pressed = set;
		break;
	default:
		/* to include the Apple keyboard left and right command keys use XK_Meta_L and XK_Meta_R respectivly. */
		if (mainloop)
			return;
	}

	if (!mainloop)
		return;

	in_move_event = 1;
	rnd_hid_notify_crosshair_change(xrnd.dsg, rnd_false);
	if (xrnd.panning)
		xrnd_pan_direct(2, e->x, e->y);
	rnd_hidcore_crosshair_move_to(xrnd.dsg, xrnd_px(e->x), xrnd_py(e->y), 1);
	if (rnd_app.adjust_attached_objects != NULL)
		rnd_app.adjust_attached_objects(xrnd.dsg);
	else
		rnd_tool_adjust_attached(xrnd.dsg);

	rnd_hid_notify_crosshair_change(xrnd.dsg, rnd_true);
	in_move_event = 0;
}

static void command_event_handler(Widget w, XtPointer p, XEvent * e, Boolean * cont)
{
	const char *hist;
	char buf[10];
	KeySym sym;

	switch (e->type) {
		case KeyRelease:
			if (cmd_is_active)
				rnd_cli_edit(xrnd.dsg);
			break;
		case KeyPress:

			/* update mod keys */
			switch (e->type) {
				case KeyPress:   ltf_mod_key((XKeyEvent *)e, 1, 0);
				case KeyRelease: ltf_mod_key((XKeyEvent *)e, 0, 0);
			}

			XLookupString((XKeyEvent *) e, buf, sizeof(buf), &sym, NULL);
			switch (sym) {
				case XK_Up:
					hist = rnd_clihist_prev();
					if (hist != NULL)
						XmTextSetString(w, XmStrCast(hist));
					else
						XmTextSetString(w, XmStrCast(""));
					break;
				case XK_Down:
					hist = rnd_clihist_next();
					if (hist != NULL)
						XmTextSetString(w, XmStrCast(hist));
					else
						XmTextSetString(w, XmStrCast(""));
					break;
				case XK_Tab:
					rnd_cli_tab(xrnd.dsg);
					*cont = False;
					break;
				case XK_Escape:
					command_hide();
					*cont = False;
					break;
			}
			break;
		}
}


static const char *lesstif_command_entry(rnd_hid_t *hid, const char *ovr, int *cursor)
{
	if (!cmd_is_active) {
		if (cursor != NULL)
			*cursor = -1;
		return NULL;
	}

	if (ovr != NULL) {
		XmTextSetString(m_cmd, XmStrCast(ovr));
		if (cursor != NULL)
			XtVaSetValues(m_cmd, XmNcursorPosition, *cursor, NULL);
	}

	if (cursor != NULL) {
		XmTextPosition pos;
		stdarg_n = 0;
		stdarg(XmNcursorPosition, &pos);
		XtGetValues(m_cmd, stdarg_args, stdarg_n);
		*cursor = pos;
	}

	return XmTextGetString(m_cmd);
}

/* ---------------------------------------------------------------------- */

static const rnd_export_opt_t *lesstif_get_export_options(rnd_hid_t *hid, int *n, rnd_design_t *dsg, void *appspec)
{
	if (n != NULL)
		*n = sizeof(lesstif_attribute_list) / sizeof(rnd_export_opt_t);
	return lesstif_attribute_list;
}

static void ltf_set_main_window_scroll(int horiz, int pos, int view, int min, int max, unsigned int slider_size)
{
	Widget s = horiz ? hscroll : vscroll;

	stdarg_n = 0;
	stdarg(XmNvalue, pos);
	stdarg(XmNsliderSize, slider_size);
	stdarg(XmNincrement, xrnd.view_zoom);
	stdarg(XmNpageIncrement, slider_size);
	stdarg(XmNminimum, min);
	stdarg(XmNmaximum, max);
	XtSetValues(s, stdarg_args, stdarg_n);
}

static void mod_changed(XKeyEvent *e, int set)
{
	ltf_mod_key(e, set, 1);
}

static rnd_hid_cfg_mod_t lesstif_mb2cfg(int but)
{
	switch(but) {
		case 1: return RND_MB_LEFT;
		case 2: return RND_MB_MIDDLE;
		case 3: return RND_MB_RIGHT;
		case 4: return RND_MB_SCROLL_UP;
		case 5: return RND_MB_SCROLL_DOWN;
	}
	return 0;
}

/* save rnd_render and restore at the end: we may get an async expose draw
   while it is set to an exporter */
#define SAVE_HID() \
do { \
	hid_save = rnd_render; \
	rnd_render = &lesstif_hid; \
} while(0)

#define RESTORE_HID() \
 rnd_render = hid_save

static void work_area_input(Widget w, XtPointer v, XEvent * e, Boolean * ctd)
{
	static int pressed_button = 0;
	rnd_hid_t *hid_save;

	xrnd_show_crosshair(0);
	switch (e->type) {
	case KeyPress:
		mod_changed(&(e->xkey), 1);
		if (lesstif_key_event(&(e->xkey))) {
			e->type = 0; /* avoid motif code to handle the key, e.g. tab pressed in drawing window */
			return;
		}
		break;

	case KeyRelease:
		mod_changed(&(e->xkey), 0);
		break;

	case ButtonPress:
		{
			int mods;
			if (pressed_button)
				return;
			/*printf("click %d\n", e->xbutton.button); */
			if (lesstif_button_event(w, e))
				return;

			rnd_hid_notify_crosshair_change(xrnd.dsg, rnd_false);
			pressed_button = e->xbutton.button;
			mods = ((e->xbutton.state & ShiftMask) ? RND_M_Shift : 0)
				+ ((e->xbutton.state & ControlMask) ? RND_M_Ctrl : 0)
#ifdef __APPLE__
				+ ((e->xbutton.state & (1 << 13)) ? RND_M_Alt : 0);
#else
				+ ((e->xbutton.state & Mod1Mask) ? RND_M_Alt : 0);
#endif
			rnd_hid_cfg_mouse_action(xrnd.dsg, &lesstif_mouse, lesstif_mb2cfg(e->xbutton.button) | mods, cmd_is_active);

			rnd_hid_notify_crosshair_change(xrnd.dsg, rnd_true);
			break;
		}

	case ButtonRelease:
		{
			int mods;
			if (e->xbutton.button != pressed_button)
				return;
			lesstif_button_event(w, e);
			rnd_hid_notify_crosshair_change(xrnd.dsg, rnd_false);
			pressed_button = 0;
			mods = ((e->xbutton.state & ShiftMask) ? RND_M_Shift : 0)
				+ ((e->xbutton.state & ControlMask) ? RND_M_Ctrl : 0)
#ifdef __APPLE__
				+ ((e->xbutton.state & (1 << 13)) ? RND_M_Alt : 0)
#else
				+ ((e->xbutton.state & Mod1Mask) ? RND_M_Alt : 0)
#endif
				+ RND_M_Release;
			rnd_hid_cfg_mouse_action(xrnd.dsg, &lesstif_mouse, lesstif_mb2cfg(e->xbutton.button) | mods, cmd_is_active);
			rnd_hid_notify_crosshair_change(xrnd.dsg, rnd_true);
			break;
		}

	case MotionNotify:
		{
			Window root, child;
			unsigned int keys_buttons;
			int root_x, root_y, pos_x, pos_y;
			while (XCheckMaskEvent(display, PointerMotionMask, e));
			XQueryPointer(display, e->xmotion.window, &root, &child, &root_x, &root_y, &pos_x, &pos_y, &keys_buttons);
			shift_pressed = (keys_buttons & ShiftMask);
			ctrl_pressed = (keys_buttons & ControlMask);
#ifdef __APPLE__
			alt_pressed = (keys_buttons & (1 << 13));
#else
			alt_pressed = (keys_buttons & Mod1Mask);
#endif
			/*rnd_printf("m %#mS %#mS\n", Px(e->xmotion.x), Py(e->xmotion.y)); */
			in_move_event = 1;
			xrnd_crosshair_move_to(pos_x, pos_y);
			in_move_event = 0;
		}
		break;

	case LeaveNotify:
		SAVE_HID();
		xrnd_crosshair_leave();
		ShowCrosshair(rnd_false);
		need_idle_proc();
		RESTORE_HID();
		break;

	case EnterNotify:
		xrnd.crosshair_in_window = 1;
		in_move_event = 1;
		xrnd_crosshair_enter(e->xcrossing.x, e->xcrossing.y);
		ShowCrosshair(rnd_true);
		in_move_event = 0;
		xrnd.need_redraw = 1;
		need_idle_proc();
		break;

	default:
		printf("work_area: unknown event %d\n", e->type);
		break;
	}

	if (cmd_is_active)
		XmProcessTraversal(m_cmd, XmTRAVERSE_CURRENT);
}

static void work_area_expose(Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs)
{
	XExposeEvent *e = &(cbs->event->xexpose);
	xrnd_work_area_expose(e->x, e->y, e->width, e->height);
}

static void scroll_callback(Widget scroll, int *view_dim, XmScrollBarCallbackStruct * cbs)
{
	*view_dim = cbs->value;
	lesstif_invalidate_all(rnd_gui);
}

static void work_area_resize(Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs)
{
	Dimension width, height;
	Pixel bgcolor;

	stdarg_n = 0;
	stdarg(XtNwidth, &width);
	stdarg(XtNheight, &height);
	stdarg(XmNbackground, &bgcolor);
	XtGetValues(work_area, stdarg_args, stdarg_n);

	xrnd_work_area_resize(width, height, bgcolor);
}

static void work_area_first_expose(Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs)
{
	Dimension width, height;
	Pixel bgcolor;

	stdarg_n = 0;
	stdarg(XtNwidth, &width);
	stdarg(XtNheight, &height);
	stdarg(XmNbackground, &bgcolor);
	XtGetValues(work_area, stdarg_args, stdarg_n);

	xrnd_work_area_first_expose(XtWindow(work_area), width, height, bgcolor);

	XtRemoveCallback(work_area, XmNexposeCallback, (XtCallbackProc) work_area_first_expose, 0);
	XtAddCallback(work_area, XmNexposeCallback, (XtCallbackProc) work_area_expose, 0);
	lesstif_invalidate_all(rnd_gui);
}

static unsigned short int lesstif_translate_key(const char *desc, int len)
{
	KeySym key;

	if (rnd_strcasecmp(desc, "enter") == 0) desc = "Return";

	key = XStringToKeysym(desc);
	if (key == NoSymbol && len > 1) {
		rnd_message(RND_MSG_INFO, "lesstif_translate_key: no symbol for %s\n", desc);
		return 0;
	}
	return key;
}

int lesstif_key_name(unsigned short int key_raw, char *out, int out_len)
{
TODO("TODO#3: do not ingore key_tr (either of them is 0)")
	char *name = XKeysymToString(key_raw);
	if (name == NULL)
		return -1;
	strncpy(out, name, out_len);
	out[out_len-1] = '\0';
	return 0;
}

void lesstif_uninit_menu(void);
void lesstif_init_menu(void);

extern Widget lesstif_menubar;
static int lesstif_hid_inited = 0;

#include "mouse.c"

static void ltf_topwin_make_menu(Widget parent)
{
	Widget menu;

	stdarg_n = 0;
	stdarg(XmNmarginWidth, 0);
	stdarg(XmNmarginHeight, 0);
	menu = lesstif_menu(parent, "menubar", stdarg_args, stdarg_n);
	XtManageChild(menu);

	lesstif_menubar = menu;
}

static void ltf_topwin_make_top(void)
{
	Widget menu_box, top_box, w;

	stdarg_n = 0;
	stdarg(PxmNfillBoxVertical, 0);
	stdarg(PxmNfillBoxFill, 1);

	top_box = PxmCreateFillBox(mainwind, XmStrCast("top_box"), stdarg_args, stdarg_n);
	XtManageChild(top_box);
	ltf_fullscreen_top = top_box;

	stdarg_n = 0;
	stdarg(PxmNfillBoxVertical, 1);
	stdarg(PxmNfillBoxFill, 1);
	menu_box = PxmCreateFillBox(top_box, XmStrCast("menu_box"), stdarg_args, stdarg_n);
	XtManageChild(menu_box);

	ltf_topwin_make_menu(menu_box);

	stdarg_n = 0;
	w = ltf_create_dockbox(menu_box, RND_HID_DOCK_TOP_LEFT, 0);
	XtManageChild(w);


	stdarg_n = 0;
	w = ltf_create_dockbox(top_box, RND_HID_DOCK_TOP_RIGHT, 0);
	XtManageChild(w);

	stdarg_n = 0;
	stdarg(XmNmenuBar, top_box);
	XtSetValues(mainwind, stdarg_args, stdarg_n);
}

static void ltf_topwin_make_drawing(void)
{
	Widget work_area_frame, w, right, horiz;

	stdarg_n = 0;
	stdarg(PxmNfillBoxVertical, 0);
	stdarg(PxmNfillBoxFill, 1);
	horiz = PxmCreateFillBox(mainwind, XmStrCast("middle_horiz"), stdarg_args, stdarg_n);
	XtManageChild(horiz);

TODO("dock: layersel depends on vertical text");
#if 1
	stdarg_n = 0;
	ltf_fullscreen_left = ltf_create_dockbox(horiz, RND_HID_DOCK_LEFT, 1);
	XtManageChild(ltf_fullscreen_left);
#else
	/* provide a 'left' widget to the full screen logics when the real left dockbox is disabled */
	stdarg_n = 0;
	ltf_fullscreen_left = PxmCreateFillBox(horiz, XmStrCast("dummy"), stdarg_args, stdarg_n);
#endif

	stdarg_n = 0;
	stdarg(PxmNfillBoxVertical, 1);
	stdarg(PxmNfillBoxFill, 1);
	right = PxmCreateFillBox(horiz, XmStrCast("middle_right"), stdarg_args, stdarg_n);
	XtManageChild(right);

	stdarg_n = 0;
	w = ltf_create_dockbox(right, RND_HID_DOCK_TOP_INFOBAR, 1);
	XtManageChild(w);

	stdarg_n = 0;
	stdarg(XmNshadowType, XmSHADOW_IN);
	stdarg(PxmNfillBoxFill, 1);
	work_area_frame = XmCreateFrame(right, XmStrCast("work_area_frame"), stdarg_args, stdarg_n);
	XtManageChild(work_area_frame);

	stdarg_n = 0;
	stdarg_do_color(&rnd_conf.appearance.color.background, XmNbackground);
	work_area = XmCreateDrawingArea(work_area_frame, XmStrCast("work_area"), stdarg_args, stdarg_n);
	XtManageChild(work_area);
	XtAddCallback(work_area, XmNexposeCallback, (XtCallbackProc) work_area_first_expose, 0);
	XtAddCallback(work_area, XmNresizeCallback, (XtCallbackProc) work_area_resize, 0);
	/* A regular callback won't work here, because lesstif swallows any
	   Ctrl<Button>1 event.  */
	XtAddEventHandler(work_area,
										ButtonPressMask | ButtonReleaseMask
										| PointerMotionMask | PointerMotionHintMask
										| KeyPressMask | KeyReleaseMask | EnterWindowMask | LeaveWindowMask, 0, work_area_input, 0);

	if (!rnd_conf.editor.unlimited_pan) {
		stdarg_n = 0;
		stdarg(XmNorientation, XmVERTICAL);
		stdarg(XmNprocessingDirection, XmMAX_ON_BOTTOM);
		stdarg(XmNminimum, xrnd.dsg->dwg.Y1 ? xrnd.dsg->dwg.Y1 : 1);
		stdarg(XmNmaximum, xrnd.dsg->dwg.Y2 ? xrnd.dsg->dwg.Y2 : 1);
		vscroll = XmCreateScrollBar(mainwind, XmStrCast("vscroll"), stdarg_args, stdarg_n);
		XtAddCallback(vscroll, XmNvalueChangedCallback, (XtCallbackProc) scroll_callback, (XtPointer)&xrnd.view_top_y);
		XtAddCallback(vscroll, XmNdragCallback, (XtCallbackProc) scroll_callback, (XtPointer)&xrnd.view_top_y);
		XtManageChild(vscroll);
	}

	if (!rnd_conf.editor.unlimited_pan) {
		stdarg_n = 0;
		stdarg(XmNorientation, XmHORIZONTAL);
		stdarg(XmNminimum, xrnd.dsg->dwg.X1 ? xrnd.dsg->dwg.X1 : 1);
		stdarg(XmNmaximum, xrnd.dsg->dwg.X2 ? xrnd.dsg->dwg.X2 : 1);
		hscroll = XmCreateScrollBar(mainwind, XmStrCast("hscroll"), stdarg_args, stdarg_n);
		XtAddCallback(hscroll, XmNvalueChangedCallback, (XtCallbackProc) scroll_callback, (XtPointer)&xrnd.view_left_x);
		XtAddCallback(hscroll, XmNdragCallback, (XtCallbackProc) scroll_callback, (XtPointer)&xrnd.view_left_x);
		XtManageChild(hscroll);
	}
}

static void ltf_topwin_make_bottom(void)
{
	stdarg_n = 0;
	stdarg(XmNresize, True);
	stdarg(XmNresizePolicy, XmRESIZE_ANY);
	messages = XmCreateForm(mainwind, XmStrCast("messages"), stdarg_args, stdarg_n);
	XtManageChild(messages);

	stdarg_n = 0;
	stdarg(XmNtopAttachment, XmATTACH_FORM);
	stdarg(XmNbottomAttachment, XmATTACH_FORM);
	stdarg(XmNleftAttachment, XmATTACH_FORM);
	stdarg(XmNrightAttachment, XmATTACH_FORM);
	stdarg(XmNalignment, XmALIGNMENT_CENTER);
	stdarg(XmNshadowThickness, 2);
	m_click = XmCreateLabel(messages, XmStrCast("click"), stdarg_args, stdarg_n);

	stdarg_n = 0;
	stdarg(XmNtopAttachment, XmATTACH_FORM);
	stdarg(XmNbottomAttachment, XmATTACH_FORM);
	stdarg(XmNleftAttachment, XmATTACH_FORM);
	stdarg(XmNlabelString, XmStringCreatePCB(rnd_cli_prompt(":")));
	m_cmd_label = XmCreateLabel(messages, XmStrCast("command"), stdarg_args, stdarg_n);

	stdarg_n = 0;
	stdarg(XmNtopAttachment, XmATTACH_FORM);
	stdarg(XmNbottomAttachment, XmATTACH_FORM);
	stdarg(XmNleftAttachment, XmATTACH_WIDGET);
	stdarg(XmNleftWidget, m_cmd_label);
	stdarg(XmNrightAttachment, XmATTACH_FORM);
	stdarg(XmNshadowThickness, 1);
	stdarg(XmNhighlightThickness, 0);
	stdarg(XmNmarginWidth, 2);
	stdarg(XmNmarginHeight, 2);
	m_cmd = XmCreateTextField(messages, XmStrCast("command"), stdarg_args, stdarg_n);
	XtAddCallback(m_cmd, XmNactivateCallback, (XtCallbackProc) command_callback, 0);
	XtAddCallback(m_cmd, XmNlosingFocusCallback, (XtCallbackProc) command_callback, 0);
	XtAddEventHandler(m_cmd, KeyPressMask | KeyReleaseMask, 0, command_event_handler, 0);

	/* status dock */
	{
		Widget w;

		stdarg_n = 0;
		stdarg(XmNtopAttachment, XmATTACH_FORM);
		stdarg(XmNbottomAttachment, XmATTACH_FORM);
		stdarg(XmNleftAttachment, XmATTACH_FORM);
		stdarg(XmNrightAttachment, XmATTACH_FORM);
		w = ltf_create_dockbox(messages, RND_HID_DOCK_BOTTOM, 0);
		XtManageChild(w);
	}

	stdarg_n = 0;
	stdarg(XmNmessageWindow, messages);
	XtSetValues(mainwind, stdarg_args, stdarg_n);

	ltf_fullscreen_bottom = messages;
}

static void lesstif_do_export(rnd_hid_t *hid, rnd_design_t *design, rnd_hid_attr_val_t *options, void *appspec)
{
	Dimension width, height;

	/* this only registers in core, safe to call before anything else */
	lesstif_init_menu();

	lft_dock_init();
	lesstif_begin();

	rnd_hid_cfg_keys_init(&lesstif_keymap);
	lesstif_keymap.translate_key = lesstif_translate_key;
	lesstif_keymap.key_name = lesstif_key_name;
	lesstif_keymap.auto_chr = 1;
	lesstif_keymap.auto_tr = rnd_hid_cfg_key_default_trans;

	stdarg_n = 0;
	stdarg(XtNwidth, &width);
	stdarg(XtNheight, &height);
	XtGetValues(appwidget, stdarg_args, stdarg_n);

	if (width < 1)
		width = 640;
	if (width > XDisplayWidth(display, screen))
		width = XDisplayWidth(display, screen);
	if (height < 1)
		height = 480;
	if (height > XDisplayHeight(display, screen))
		height = XDisplayHeight(display, screen);

	stdarg_n = 0;
	stdarg(XmNwidth, width);
	stdarg(XmNheight, height);
	XtSetValues(appwidget, stdarg_args, stdarg_n);

	stdarg_n = 0;
	stdarg(XmNspacing, 0);
	mainwind = XmCreateMainWindow(appwidget, XmStrCast("mainWind"), stdarg_args, stdarg_n);
	XtManageChild(mainwind);


	ltf_topwin_make_drawing();
	ltf_topwin_make_top();
	ltf_topwin_make_bottom();


	if ((ltf_values[HA_bg_image].str != NULL) && (*ltf_values[HA_bg_image].str != '\0'))
		xrnd_load_old_bgimage(ltf_values[HA_bg_image].str);

	XtRealizeWidget(appwidget);
	rnd_ltf_winplace(display, XtWindow(appwidget), "top", 640, 480);
	XtAddEventHandler(appwidget, StructureNotifyMask, False, rnd_ltf_wplc_config_cb, "top");

	while (!xrnd.window) {
		XEvent e;
		XtAppNextEvent(app_context, &e);
		XtDispatchEvent(&e);
	}

	rnd_hid_announce_gui_init(xrnd.dsg);
	XmProcessTraversal(work_area, XmTRAVERSE_CURRENT);


	lesstif_hid_inited = 1;

	XtAppMainLoop(app_context);

	rnd_hid_cfg_keys_uninit(&lesstif_keymap);
	lesstif_end();
}

static void lesstif_do_exit(rnd_hid_t *hid)
{
	lesstif_attr_dlg_free_all();
	XtAppSetExitFlag(app_context);
	lft_dock_uninit();
}

static void lesstif_uninit(rnd_hid_t *hid)
{
	if (lesstif_hid_inited) {
		lesstif_uninit_menu();
		ltf_mouse_uninit();
		lesstif_hid_inited = 0;
	}
}

static void lesstif_iterate(rnd_hid_t *hid)
{
	while (XtAppPending(app_context))
		XtAppProcessEvent(app_context, XtIMAll);
}

typedef union {
	int i;
	double f;
	char *s;
	rnd_coord_t c;
} val_union;

static Boolean
rnd_cvt_string_to_double(Display * d, XrmValue * args, Cardinal * num_args, XrmValue * from, XrmValue * to, XtPointer * data)
{
	static double rv;
	rv = strtod((char *) from->addr, 0);
	if (to->addr)
		*(double *) to->addr = rv;
	else
		to->addr = (XPointer) & rv;
	to->size = sizeof(rv);
	return True;
}

static Boolean
rnd_cvt_string_to_coord(Display * d, XrmValue * args, Cardinal * num_args, XrmValue * from, XrmValue * to, XtPointer * data)
{
	static rnd_coord_t rv;
	rv = rnd_get_value((char *) from->addr, NULL, NULL, NULL);
	if (to->addr)
		*(rnd_coord_t *) to->addr = rv;
	else
		to->addr = (XPointer) & rv;
	to->size = sizeof(rv);
	return TRUE;
}

static void mainwind_delete_cb()
{
	/* do not call lesstif_attr_dlg_free_all(gctx) here because Quit may cancel
	   and keep the app running */
	rnd_action(xrnd.dsg, "Quit");
}

static void lesstif_listener_cb(XtPointer client_data, int *fid, XtInputId * id)
{
	char buf[BUFSIZ];
	int nbytes;

	if ((nbytes = read(*fid, buf, BUFSIZ)) == -1)
		perror("lesstif_listener_cb");

	if (nbytes) {
		buf[nbytes] = '\0';
		rnd_parse_actions(xrnd.dsg, buf);
	}
}

static jmp_buf lesstif_err_jmp;
static void lesstif_err_msg(String name, String type, String class, String dflt, String *params, Cardinal *num_params)
{
	char *par[8];
	int n;
	for(n = 0; n < 8; n++) par[n] = "";
	for(n = 0; n < *num_params; n++) par[n] = params[n];
	fprintf(stderr, "Lesstif/motif initializaion error:\n");
	fprintf(stderr, dflt, par[0], par[1], par[2], par[3], par[4], par[5], par[6], par[7]);
	fprintf(stderr, "\n");
	longjmp(lesstif_err_jmp, 1);
}

static int lesstif_parse_arguments(rnd_hid_t *hid, int *argc, char ***argv)
{
	Atom close_atom;
	rnd_hid_attr_node_t *ha;
	int acount = 0, amax;
	int rcount = 0, rmax;
	int i, err;
	XrmOptionDescRec *new_options;
	XtResource *new_resources;
	val_union *new_values;

	XtSetTypeConverter(XtRString, XtRDouble, rnd_cvt_string_to_double, NULL, 0, XtCacheAll, NULL);
	XtSetTypeConverter(XtRString, XtRPCBCoord, rnd_cvt_string_to_coord, NULL, 0, XtCacheAll, NULL);

	lesstif_reg_attrs();

	for (ha = rnd_hid_attr_nodes; ha; ha = ha->next)
		for (i = 0; i < ha->n; i++) {
			const rnd_export_opt_t *a = ha->opts + i;
			switch (a->type) {
			case RND_HATT_INTEGER:
			case RND_HATT_COORD:
			case RND_HATT_REAL:
			case RND_HATT_STRING:
			case RND_HATT_BOOL:
				acount++;
				rcount++;
				break;
			default:
				break;
			}
		}

#if 0
	amax = acount + XtNumber(lesstif_options);
#else
	amax = acount;
#endif

	new_options = (XrmOptionDescRec *) malloc((amax + 1) * sizeof(XrmOptionDescRec));

#if 0
	memcpy(new_options + acount, lesstif_options, sizeof(lesstif_options));
#endif
	acount = 0;

	rmax = rcount;

	new_resources = (XtResource *) malloc((rmax + 1) * sizeof(XtResource));
	new_values = (val_union *) malloc((rmax + 1) * sizeof(val_union));
	rcount = 0;

	for (ha = rnd_hid_attr_nodes; ha; ha = ha->next)
		for (i = 0; i < ha->n; i++) {
			const rnd_export_opt_t *a = ha->opts + i;
			rnd_hid_attr_val_t *val = &ha->hid->argument_array[i];
			XrmOptionDescRec *o = new_options + acount;
			char *tmpopt, *tmpres;
			XtResource *r = new_resources + rcount;

			tmpopt = (char *) malloc(strlen(a->name) + 3);
			tmpopt[0] = tmpopt[1] = '-';
			strcpy(tmpopt + 2, a->name);
			o->option = tmpopt;

			tmpres = (char *) malloc(strlen(a->name) + 2);
			tmpres[0] = '*';
			strcpy(tmpres + 1, a->name);
			o->specifier = tmpres;

			switch (a->type) {
			case RND_HATT_INTEGER:
			case RND_HATT_COORD:
			case RND_HATT_REAL:
			case RND_HATT_STRING:
				o->argKind = XrmoptionSepArg;
				o->value = NULL;
				acount++;
				break;
			case RND_HATT_BOOL:
				o->argKind = XrmoptionNoArg;
				o->value = XmStrCast("True");
				acount++;
				break;
			default:
				break;
			}

			r->resource_name = XmStrCast(a->name);
			r->resource_class = XmStrCast(a->name);
			r->resource_offset = sizeof(val_union) * rcount;

			switch (a->type) {
			case RND_HATT_INTEGER:
				r->resource_type = XtRInt;
				r->default_type = XtRInt;
				r->resource_size = sizeof(int);
				r->default_addr = &(val->lng);
				rcount++;
				break;
			case RND_HATT_COORD:
				r->resource_type = XmStrCast(XtRPCBCoord);
				r->default_type = XmStrCast(XtRPCBCoord);
				r->resource_size = sizeof(rnd_coord_t);
				r->default_addr = &(val->crd);
				rcount++;
				break;
			case RND_HATT_REAL:
				r->resource_type = XmStrCast(XtRDouble);
				r->default_type = XmStrCast(XtRDouble);
				r->resource_size = sizeof(double);
				r->default_addr = &(val->dbl);
				rcount++;
				break;
			case RND_HATT_STRING:
				r->resource_type = XtRString;
				r->default_type = XtRString;
				r->resource_size = sizeof(char *);
				r->default_addr = (char *) val->str;
				rcount++;
				break;
			case RND_HATT_BOOL:
				r->resource_type = XtRBoolean;
				r->default_type = XtRInt;
				r->resource_size = sizeof(int);
				r->default_addr = &(val->lng);
				rcount++;
				break;
			default:
				break;
			}
		}

	stdarg_n = 0;
	stdarg(XmNdeleteResponse, XmDO_NOTHING);

	XtSetErrorMsgHandler(lesstif_err_msg);
	err = setjmp(lesstif_err_jmp);
	if (err != 0)
		return err;
	appwidget = XtAppInitialize(&app_context, rnd_app.package, new_options, amax, argc, *argv, 0, stdarg_args, stdarg_n);
	if (appwidget == NULL)
		return 1;
	XtSetErrorMsgHandler(NULL); /* restore the default handler */

	display = XtDisplay(appwidget);
	screen_s = XtScreen(appwidget);
	screen = XScreenNumberOfScreen(screen_s);

	close_atom = XmInternAtom(display, XmStrCast("WM_DELETE_WINDOW"), 0);
	XmAddWMProtocolCallback(appwidget, close_atom, (XtCallbackProc) mainwind_delete_cb, 0);

	/*  XSynchronize(display, True); */

	XtGetApplicationResources(appwidget, new_values, new_resources, rmax, 0, 0);

	xrnd_init(display, screen, &lesstif_hid, ltf_set_main_window_scroll, lesstif_need_idle_proc);

	rnd_hid_parse_command_line(argc, argv);

	/* redefine xrnd.colormap, if requested via "-install" */
	if (ltf_values[HA_colormap].lng) {
		xrnd.colormap = XCopyColormapAndFree(display, xrnd.colormap);
		XtVaSetValues(appwidget, XtNcolormap, xrnd.colormap, NULL);
	}

	/* listen on standard input for actions */
	if (ltf_values[HA_listen].lng) {
		XtAppAddInput(app_context, rnd_fileno(stdin), (XtPointer) XtInputReadMask, lesstif_listener_cb, NULL);
	}
	return 0;
}

static Boolean idle_proc(XtPointer dummy)
{
	rnd_hid_t *hid_save;

	SAVE_HID();
	if (xrnd.need_redraw) {
		xrnd_redraw_main(&lesstif_hid);
		rnd_ltf_preview_invalidate(NULL);
	}

TODO(": remove this, update-on should handle all cases")
	lesstif_update_widget_flags(NULL, NULL);

	xrnd_show_crosshair(1);
	idle_proc_set = 0;
	RESTORE_HID();
	return True;
}

void lesstif_need_idle_proc()
{
	if (idle_proc_set || xrnd.window == 0)
		return;
	XtAppAddWorkProc(app_context, idle_proc, 0);
	idle_proc_set = 1;
}

static void lesstif_invalidate_lr(rnd_hid_t *hid, rnd_coord_t l, rnd_coord_t r, rnd_coord_t t, rnd_coord_t b)
{
	if (!xrnd.window)
		return;

	xrnd.need_redraw = 1;
	need_idle_proc();
}

void lesstif_invalidate_all(rnd_hid_t *hid)
{
	if (xrnd.dsg != NULL)
		lesstif_invalidate_lr(hid, xrnd.dsg->dwg.X1, xrnd.dsg->dwg.X2, xrnd.dsg->dwg.Y1, xrnd.dsg->dwg.Y2);
}

static int lesstif_set_layer_group(rnd_hid_t *hid, rnd_design_t *design, rnd_layergrp_id_t group, const char *purpose, int purpi, rnd_layer_id_t layer, unsigned int flags, int is_empty, rnd_xform_t **xform)
{
	/* accept anything and draw */
	return 1;
}

static int lesstif_shift_is_pressed(rnd_hid_t *hid)
{
	return shift_pressed;
}

static int lesstif_control_is_pressed(rnd_hid_t *hid)
{
	return ctrl_pressed;
}

static int lesstif_mod1_is_pressed(rnd_hid_t *hid)
{
	return alt_pressed;
}

extern int lesstif_get_coords(rnd_hid_t *hid, const char *msg, rnd_coord_t *x, rnd_coord_t *y, int force);

static void lesstif_set_crosshair(rnd_hid_t *hid, rnd_coord_t x, rnd_coord_t y, rnd_set_crosshair_t action)
{
	xrnd_set_crosshair(x, y, action, (mainwind && !in_move_event), &in_move_event);
}

typedef struct {
	void (*func) (rnd_hidval_t);
	rnd_hidval_t user_data;
	XtIntervalId id;
} TimerStruct;

static void lesstif_timer_cb(XtPointer * p, XtIntervalId * id)
{
	TimerStruct *ts = (TimerStruct *) p;
	ts->func(ts->user_data);
	free(ts);
}

static rnd_hidval_t lesstif_add_timer(rnd_hid_t *hid, void (*func)(rnd_hidval_t user_data), unsigned long milliseconds, rnd_hidval_t user_data)
{
	TimerStruct *t;
	rnd_hidval_t rv;
	t = (TimerStruct *) malloc(sizeof(TimerStruct));
	rv.ptr = t;
	t->func = func;
	t->user_data = user_data;
	t->id = XtAppAddTimeOut(app_context, milliseconds, (XtTimerCallbackProc) lesstif_timer_cb, t);
	return rv;
}

static void lesstif_stop_timer(rnd_hid_t *hid, rnd_hidval_t hv)
{
	TimerStruct *ts = (TimerStruct *) hv.ptr;
	XtRemoveTimeOut(ts->id);
	free(ts);
}


typedef struct {
	rnd_bool (*func) (rnd_hidval_t, int, unsigned int, rnd_hidval_t);
	rnd_hidval_t user_data;
	int fd;
	XtInputId id;
} WatchStruct;

void lesstif_unwatch_file(rnd_hid_t *hid, rnd_hidval_t data)
{
	WatchStruct *watch = (WatchStruct *) data.ptr;
	XtRemoveInput(watch->id);
	free(watch);
}

/* We need a wrapper around the hid file watch because to pass the correct flags */
static void lesstif_watch_cb(XtPointer client_data, int *fid, XtInputId *id)
{
	unsigned int rnd_condition = 0;
	struct pollfd fds;
	short condition;
	rnd_hidval_t x;
	WatchStruct *watch = (WatchStruct *) client_data;

	fds.fd = watch->fd;
	fds.events = POLLIN | POLLOUT;
	poll(&fds, 1, 0);
	condition = fds.revents;

	/* Should we only include those we were asked to watch? */
	if (condition & POLLIN)
		rnd_condition |= RND_WATCH_READABLE;
	if (condition & POLLOUT)
		rnd_condition |= RND_WATCH_WRITABLE;
	if (condition & POLLERR)
		rnd_condition |= RND_WATCH_ERROR;
	if (condition & POLLHUP)
		rnd_condition |= RND_WATCH_HANGUP;

	x.ptr = (void *) watch;
	if (!watch->func(x, watch->fd, rnd_condition, watch->user_data))
		lesstif_unwatch_file(rnd_gui, x);
	return;
}

rnd_hidval_t lesstif_watch_file(rnd_hid_t *hid, int fd, unsigned int condition, rnd_bool (*func)(rnd_hidval_t watch, int fd, unsigned int condition, rnd_hidval_t user_data), rnd_hidval_t user_data)
{
	WatchStruct *watch = (WatchStruct *)malloc(sizeof(WatchStruct));
	rnd_hidval_t ret;
	unsigned int xt_condition = 0;

	if (condition & RND_WATCH_READABLE)
		xt_condition |= XtInputReadMask;
	if (condition & RND_WATCH_WRITABLE)
		xt_condition |= XtInputWriteMask;
	if (condition & RND_WATCH_ERROR)
		xt_condition |= XtInputExceptMask;
	if (condition & RND_WATCH_HANGUP)
		xt_condition |= XtInputExceptMask;

	watch->func = func;
	watch->user_data = user_data;
	watch->fd = fd;
	watch->id = XtAppAddInput(app_context, fd, (XtPointer) (size_t) xt_condition, lesstif_watch_cb, watch);

	ret.ptr = (void *) watch;
	return ret;
}

extern void lesstif_attr_dlg_new(rnd_hid_t *hid, const char *id, rnd_hid_attribute_t *attrs_, int n_attrs_, const char *title_, void *caller_data, rnd_bool modal, void (*button_cb)(void *caller_data, rnd_hid_attr_ev_t ev), int defx, int defy, int minx, int miny, void **hid_ctx_out);

extern int lesstif_attr_dlg_run(void *hid_ctx);
extern void lesstif_attr_dlg_raise(void *hid_ctx);
extern void lesstif_attr_dlg_close(void *hid_ctx);
extern void lesstif_attr_dlg_free(void *hid_ctx);
extern void lesstif_attr_dlg_property(void *hid_ctx, rnd_hat_property_t prop, const rnd_hid_attr_val_t *val);
extern int lesstif_attr_dlg_widget_state(void *hid_ctx, int idx, int enabled);
extern int lesstif_attr_dlg_widget_hide(void *hid_ctx, int idx, rnd_bool hide);
extern int lesstif_attr_dlg_widget_focus(void *hid_ctx, int idx);
extern int lesstif_attr_dlg_widget_poke(void *hid_ctx, int idx, int argc, fgw_arg_t argv[]);
extern int lesstif_attr_dlg_set_value(void *hid_ctx, int idx, const rnd_hid_attr_val_t *val);
extern void lesstif_attr_dlg_set_help(void *hid_ctx, int idx, const char *val);


#include "wt_preview.c"

static int lesstif_usage(rnd_hid_t *hid, const char *topic)
{
	fprintf(stderr, "\nLesstif GUI command line arguments:\n\n");
	rnd_hid_usage(lesstif_attribute_list, sizeof(lesstif_attribute_list) / sizeof(lesstif_attribute_list[0]));
	fprintf(stderr, "\nInvocation: %s --gui lesstif [options]\n", rnd_app.package);
	return 0;
}

static void lesstif_globconf_change_post(rnd_conf_native_t *cfg, int arr_idx, void *user_data)
{
	if (!lesstif_active)
		return;
	if (strncmp(cfg->hash_path, "appearance/color/", 17) == 0)
		lesstif_invalidate_all(rnd_gui);
	if (strncmp(cfg->hash_path, "rc/cli_", 7) == 0) {
		stdarg_n = 0;
		stdarg(XmNlabelString, XmStringCreatePCB(rnd_cli_prompt(":")));
		XtSetValues(m_cmd_label, stdarg_args, stdarg_n);
	}
}

static void ltf_confchg_fullscreen(rnd_conf_native_t *cfg, int arr_idx, void *user_data)
{
	if (!lesstif_active)
		return;

	if (rnd_conf.editor.fullscreen) {
		XtUnmanageChild(ltf_fullscreen_top);
		XtUnmanageChild(ltf_fullscreen_left);
		XtUnmanageChild(ltf_fullscreen_bottom);
	}
	else {
		XtManageChild(ltf_fullscreen_top);
		XtManageChild(ltf_fullscreen_left);
		XtManageChild(ltf_fullscreen_bottom);
	}
}

static rnd_conf_hid_id_t lesstif_conf_id = -1;

static void init_conf_watch(rnd_conf_hid_callbacks_t *cbs, const char *path, void (*func)(rnd_conf_native_t *, int, void *))
{
	rnd_conf_native_t *n = rnd_conf_get_field(path);
	if (n != NULL) {
		memset(cbs, 0, sizeof(rnd_conf_hid_callbacks_t));
		cbs->val_change_post = func;
		rnd_conf_hid_set_cb(n, lesstif_conf_id, cbs);
	}
}

static void lesstif_conf_regs(const char *cookie)
{
	static rnd_conf_hid_callbacks_t cbs_fullscreen;
	init_conf_watch(&cbs_fullscreen, "editor/fullscreen", ltf_confchg_fullscreen);
}


#include <Xm/CutPaste.h>

/* render.c doesn't handle clipboard (because miniboxtk has an API for this) */
static int ltf_clip_set(rnd_hid_t *hid, const char *str)
{
	static long cnt = 0;
	long item_id, data_id;
	XmString lab = XmStringCreateLocalized((char *)rnd_app.package);
	
	
	if (XmClipboardStartCopy(display, xrnd.window, lab, CurrentTime, 0, NULL, &item_id) != XmClipboardSuccess) {
		XmStringFree(lab);
		return -1;
	}
	XmStringFree(lab);
	if (XmClipboardCopy(display, xrnd.window, item_id, "STRING", (void *)str, strlen(str), ++cnt, &data_id) != XmClipboardSuccess) {
		XmClipboardCancelCopy(display, xrnd.window, item_id);
		return -1;
	}
	if (XmClipboardEndCopy(display, xrnd.window, item_id) != XmClipboardSuccess) {
		XmClipboardCancelCopy(display, xrnd.window, item_id);
		return -1;
	}
	return 0;
}

static char *ltf_clip_get(rnd_hid_t *hid)
{
	int res;
	gds_t tmp = {0};
	char buff[65536];
	long unsigned bl = 0;
	long dummy;

	if (XmClipboardStartRetrieve(display, xrnd.window, CurrentTime) != XmClipboardSuccess)
		return NULL;

	res = XmClipboardRetrieve(display, xrnd.window, "STRING", buff, sizeof(buff), &bl, &dummy);
	if (res == XmClipboardSuccess) {
		if (bl > 0)
			gds_append_len(&tmp, buff, bl);
	}

	XmClipboardEndRetrieve(display, xrnd.window);
	return tmp.array;
}

static void ltf_open_command(rnd_hid_t *hid)
{
	rnd_clihist_init();
	rnd_clihist_reset();

	if (rnd_conf.editor.fullscreen)
		XtManageChild(ltf_fullscreen_bottom);

	/* command_show(): */
	XtManageChild(m_cmd_label);
	XtManageChild(m_cmd);
	XmProcessTraversal(m_cmd, XmTRAVERSE_CURRENT);
	cmd_is_active = 1;
}

static void ltf_set_top_title(rnd_hid_t *hid, const char *title)
{
	stdarg_n = 0;
	stdarg(XmNtitle, title);
	XtSetValues(appwidget, stdarg_args, stdarg_n);
}

int lesstif_create_menu_widget(rnd_hid_t *ctx, int is_popup_ignore, const char *name, int is_main, lht_node_t *parent, lht_node_t *ins_after, lht_node_t *menu_item);
int lesstif_remove_menu_node(rnd_hid_t *hid, lht_node_t *node);
rnd_hid_cfg_t *lesstif_get_menu_cfg(rnd_hid_t *hid);
int ltf_open_popup(rnd_hid_t *hid, const char *menupath);

int pplg_check_ver_hid_lesstif(int version_we_need) { return 0; }

void pplg_uninit_hid_lesstif(void)
{
	rnd_export_remove_opts_by_cookie(lesstif_cookie);
	rnd_event_unbind_allcookie(lesstif_cookie);
	rnd_conf_hid_unreg(lesstif_cookie);
}

int pplg_init_hid_lesstif(void)
{
	static rnd_conf_hid_callbacks_t ccb;

	RND_API_CHK_VER;

	memset(&ccb, 0, sizeof(ccb));
	ccb.val_change_post = lesstif_globconf_change_post;

	memset(&lesstif_hid, 0, sizeof(rnd_hid_t));

	rnd_hid_nogui_init(&lesstif_hid);

	lesstif_hid.struct_size = sizeof(rnd_hid_t);
	lesstif_hid.name = "lesstif";
	lesstif_hid.description = "LessTif - a Motif clone for X/Unix";
	lesstif_hid.gui = 1;

	lesstif_hid.get_export_options = lesstif_get_export_options;
	lesstif_hid.do_export = lesstif_do_export;
	lesstif_hid.do_exit = lesstif_do_exit;
	lesstif_hid.uninit = lesstif_uninit;
	lesstif_hid.iterate = lesstif_iterate;
	lesstif_hid.parse_arguments = lesstif_parse_arguments;
	lesstif_hid.invalidate_lr = lesstif_invalidate_lr;
	lesstif_hid.invalidate_all = lesstif_invalidate_all;
	lesstif_hid.notify_crosshair_change = xrnd_notify_crosshair_change;
	lesstif_hid.notify_mark_change = xrnd_notify_mark_change;
	lesstif_hid.set_layer_group = lesstif_set_layer_group;
	lesstif_hid.make_gc = xrnd_make_gc;
	lesstif_hid.destroy_gc = xrnd_destroy_gc;
	lesstif_hid.set_drawing_mode = xrnd_set_drawing_mode;
	lesstif_hid.render_burst = xrnd_render_burst;
	lesstif_hid.set_color = xrnd_set_color;
	lesstif_hid.set_line_cap = xrnd_set_line_cap;
	lesstif_hid.set_line_width = xrnd_set_line_width;
	lesstif_hid.set_draw_xor = xrnd_set_draw_xor;
	lesstif_hid.draw_line = xrnd_draw_line;
	lesstif_hid.draw_arc = xrnd_draw_arc;
	lesstif_hid.draw_rect = xrnd_draw_rect;
	lesstif_hid.fill_circle = xrnd_fill_circle;
	lesstif_hid.fill_polygon = xrnd_fill_polygon;
	lesstif_hid.fill_polygon_offs = xrnd_fill_polygon_offs;
	lesstif_hid.fill_rect = xrnd_fill_rect;

	lesstif_hid.shift_is_pressed = lesstif_shift_is_pressed;
	lesstif_hid.control_is_pressed = lesstif_control_is_pressed;
	lesstif_hid.mod1_is_pressed = lesstif_mod1_is_pressed;
	lesstif_hid.get_coords = lesstif_get_coords;
	lesstif_hid.set_crosshair = lesstif_set_crosshair;
	lesstif_hid.add_timer = lesstif_add_timer;
	lesstif_hid.stop_timer = lesstif_stop_timer;
	lesstif_hid.watch_file = lesstif_watch_file;
	lesstif_hid.unwatch_file = lesstif_unwatch_file;
	lesstif_hid.benchmark = xrnd_benchmark;

	lesstif_hid.attr_dlg_new = lesstif_attr_dlg_new;
	lesstif_hid.attr_dlg_run = lesstif_attr_dlg_run;
	lesstif_hid.attr_dlg_raise = lesstif_attr_dlg_raise;
	lesstif_hid.attr_dlg_close = lesstif_attr_dlg_close;
	lesstif_hid.attr_dlg_free = lesstif_attr_dlg_free;
	lesstif_hid.attr_dlg_property = lesstif_attr_dlg_property;
	lesstif_hid.attr_dlg_widget_state = lesstif_attr_dlg_widget_state;
	lesstif_hid.attr_dlg_widget_hide = lesstif_attr_dlg_widget_hide;
	lesstif_hid.attr_dlg_widget_poke = lesstif_attr_dlg_widget_poke;
	lesstif_hid.attr_dlg_widget_focus = lesstif_attr_dlg_widget_focus;
	lesstif_hid.attr_dlg_set_value = lesstif_attr_dlg_set_value;
	lesstif_hid.attr_dlg_set_help = lesstif_attr_dlg_set_help;
	lesstif_hid.supports_dad_text_markup = 0;

	lesstif_hid.command_entry = lesstif_command_entry;
	lesstif_hid.clip_set = ltf_clip_set;
	lesstif_hid.clip_get = ltf_clip_get;

	lesstif_hid.create_menu_by_node = lesstif_create_menu_widget;
	lesstif_hid.remove_menu_node = lesstif_remove_menu_node;
	lesstif_hid.update_menu_checkbox = lesstif_update_widget_flags;
	lesstif_hid.get_menu_cfg = lesstif_get_menu_cfg;

	lesstif_hid.key_state = &lesstif_keymap;

	lesstif_hid.zoom_win = xrnd_zoom_win;
	lesstif_hid.zoom = xrnd_zoom;
	lesstif_hid.pan = xrnd_pan;
	lesstif_hid.pan_mode = xrnd_pan_mode;
	lesstif_hid.view_get = xrnd_view_get;
	lesstif_hid.open_command = ltf_open_command;
	lesstif_hid.open_popup = ltf_open_popup;
	lesstif_hid.reg_mouse_cursor = ltf_reg_mouse_cursor;
	lesstif_hid.set_mouse_cursor = ltf_set_mouse_cursor;
	lesstif_hid.set_top_title = ltf_set_top_title;
	lesstif_hid.dock_enter = ltf_dock_enter;
	lesstif_hid.dock_leave = ltf_dock_leave;

	lesstif_hid.draw_pixmap = xrnd_draw_pixmap;
	lesstif_hid.uninit_pixmap = xrnd_uninit_pixmap;

	lesstif_hid.set_design = ltf_set_hidlib;

	lesstif_hid.usage = lesstif_usage;

	lesstif_hid.get_dad_design = ltf_attr_get_dad_hidlib;

	lesstif_hid.argument_array = ltf_values;

	rnd_hid_register_hid(&lesstif_hid);
	if (lesstif_conf_id < 0)
		lesstif_conf_id = rnd_conf_hid_reg(lesstif_cookie, &ccb);

	lesstif_conf_regs(lesstif_cookie);
	return 0;
}

static int lesstif_attrs_regd = 0;
static void lesstif_reg_attrs(void)
{
	if (!lesstif_attrs_regd)
		rnd_export_register_opts2(&lesstif_hid, lesstif_attribute_list, sizeof(lesstif_attribute_list)/sizeof(lesstif_attribute_list[0]), lesstif_cookie, 0);
	lesstif_attrs_regd = 1;
}

extern void rnd_ltf_library_init2(void);
extern void rnd_ltf_dialogs_init2(void);
extern void rnd_ltf_netlist_init2(void);


static void lesstif_begin(void)
{
	lesstif_reg_attrs();
	rnd_ltf_dialogs_init2();
	lesstif_active = 1;
}

static void lesstif_end(void)
{
	rnd_remove_actions_by_cookie(lesstif_cookie);
	rnd_export_remove_opts_by_cookie(lesstif_cookie);
	lesstif_active = 0;
}
