Hi there,

I recently switched to irssi and I wanted to change some of its
behaviour wrt split windows. I discovered they are not supported in the
Perl interface at all. So I added some.

While doing that, I discovered that there is no consideration for
cyclical data structures: if they are encountered an infinite Perl data
structure is constructed and you crash on a stack overflow due to the
recursion. Similarly, if the same C structure is converted twice you get
different Perl objects. This is wasteful of time and memory. You can
also never find identical objects by comparing their references. I have
started to add some code for handling that but it is #ifdeffed out (I
need some way to detect when a perl object is destroyed so I can remove
it from my administration). For now, to break the cycle in the Window
<-> MainWindow references, I used an access function instead of a data
member.

Here is a diff.

There seems to be a breach of abstraction because a TextUI::MainWindow
(which is in fe-text) is accessed from a UI::Window, requiring an
include from a different subdirectory. But that's where the initial
parts of MainWindow were, I presume with reason.

Statusbar seemed missing too so I added some stuff for that too.

Index: src/perl/textui/typemap
===================================================================
--- src/perl/textui/typemap     (revision 4172)
+++ src/perl/textui/typemap     (working copy)
@@ -5,6 +5,7 @@
 Irssi::TextUI::Line            T_PlainObj
 Irssi::TextUI::LineCache       T_PlainObj
 Irssi::TextUI::LineInfo                T_PlainObj
+Irssi::TextUI::Statusbar       T_PlainObj
 Irssi::TextUI::StatusbarItem           T_PlainObj
 
 INPUT
Index: src/perl/textui/TextUI.xs
===================================================================
--- src/perl/textui/TextUI.xs   (revision 4172)
+++ src/perl/textui/TextUI.xs   (working copy)
@@ -8,13 +8,24 @@
 static void perl_main_window_fill_hash(HV *hv, MAIN_WINDOW_REC *window)
 {
        hv_store(hv, "active", 6, plain_bless(window->active, 
"Irssi::UI::Window"), 0);
+       /* TERM_WINDOW *screen_win; TODO
+       if (window->screen_win)
+               hv_store(hv, "screen_win", 10, plain_bless(window->screen_win, 
"Irssi::UI:TerminalWindow"), 0);
+       */
 
+       hv_store(hv, "sticky_windows", 14, newSViv(window->sticky_windows), 0);
+       /* GSList *statusbars; via $win->statusbars() */
        hv_store(hv, "first_line", 10, newSViv(window->first_line), 0);
        hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
        hv_store(hv, "width", 5, newSViv(window->width), 0);
        hv_store(hv, "height", 6, newSViv(window->height), 0);
 
+       hv_store(hv, "statusbar_lines_top", 19, 
newSViv(window->statusbar_lines_top), 0);
+       hv_store(hv, "statusbar_lines_bottom", 22, 
newSViv(window->statusbar_lines_bottom), 0);
        hv_store(hv, "statusbar_lines", 15, newSViv(window->statusbar_lines), 
0);
+
+       hv_store(hv, "dirty", 5, newSViv(window->dirty), 0);
+       hv_store(hv, "size_dirty", 10, newSViv(window->size_dirty), 0);
 }
 
 static void perl_text_buffer_fill_hash(HV *hv, TEXT_BUFFER_REC *buffer)
@@ -66,6 +77,22 @@
        hv_store(hv, "time", 4, newSViv(info->time), 0);
 }
 
+static void perl_statusbar_fill_hash(HV *hv, STATUSBAR_REC *bar)
+{
+       /* TODO: STATUSBAR_GROUP_REC *group; */
+       /* TODO: STATUSBAR_CONFIG_REC *config; */
+       /* via $bar->parent_window() 
+       if (bar->parent_window != NULL)
+               hv_store(hv, "parent_window", 13, 
plain_bless(bar->parent_window, "Irssi::TextUI::MainWindow"), 0);
+       */
+       /* GSList *items; via $bar->items() */
+
+       hv_store(hv, "color", 5, new_pv(bar->color), 0);
+       hv_store(hv, "real_ypos", 8, newSViv(bar->real_ypos), 0);
+       hv_store(hv, "dirty", 5, newSViv(bar->dirty), 0);
+       hv_store(hv, "dirty_xpos", 10, newSViv(bar->dirty_xpos), 0);
+}
+
 static void perl_statusbar_item_fill_hash(HV *hv, SBAR_ITEM_REC *item)
 {
        hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
@@ -74,6 +101,9 @@
        hv_store(hv, "size", 4, newSViv(item->size), 0);
        if (item->bar->parent_window != NULL)
                hv_store(hv, "window", 6, 
plain_bless(item->bar->parent_window->active, "Irssi::UI::Window"), 0);
+       /*
+       hv_store(hv, "bar", 3, plain_bless(item->bar, 
"Irssi::TextUI::Statusbar"), 0);
+       */
 }
 
 static PLAIN_OBJECT_INIT_REC textui_plains[] = {
@@ -83,6 +113,7 @@
        { "Irssi::TextUI::Line", (PERL_OBJECT_FUNC) perl_line_fill_hash },
        { "Irssi::TextUI::LineCache", (PERL_OBJECT_FUNC) 
perl_line_cache_fill_hash },
        { "Irssi::TextUI::LineInfo", (PERL_OBJECT_FUNC) 
perl_line_info_fill_hash },
+       { "Irssi::TextUI::Statusbar", (PERL_OBJECT_FUNC) 
perl_statusbar_fill_hash },
        { "Irssi::TextUI::StatusbarItem", (PERL_OBJECT_FUNC) 
perl_statusbar_item_fill_hash },
 
        { NULL, NULL }
@@ -164,3 +195,95 @@
 
 void
 term_refresh_thaw()
+
+MODULE = Irssi::TextUI::MainWindow  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+mainwindows()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, 
"Irssi::TextUI::MainWindow")));
+       }
+
+
+Irssi::TextUI::MainWindow
+active_mainwin()
+CODE:
+       RETVAL = active_mainwin;
+OUTPUT:
+       RETVAL
+
+int
+window_refnum_left(refnum, wrap)
+       int refnum
+       int wrap
+
+int
+window_refnum_right(refnum, wrap)
+       int refnum
+       int wrap
+
+#*******************************
+MODULE = Irssi::TextUI::MainWindow  PACKAGE = Irssi::TextUI::MainWindow  
PREFIX=mainwindow_
+#*******************************
+
+Irssi::TextUI::MainWindow
+mainwindow_up(mainwin, wrap)
+       Irssi::TextUI::MainWindow mainwin
+       int wrap
+
+Irssi::TextUI::MainWindow
+mainwindow_down(mainwin, wrap)
+       Irssi::TextUI::MainWindow mainwin
+       int wrap
+
+void
+statusbars(mainwin)
+       Irssi::TextUI::MainWindow mainwin;
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = mainwin->statusbars; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, 
"Irssi::TextUI::Statusbar")));
+       }
+
+#*******************************
+MODULE = Irssi::TextUI::Statusbar  PACKAGE = Irssi::TextUI::Statusbar
+#*******************************
+
+void
+items(bar)
+       Irssi::TextUI::Statusbar bar;
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, 
"Irssi::TextUI::StatusbarItem")));
+       }
+
+Irssi::TextUI::MainWindow
+parent_window(bar)
+       Irssi::TextUI::Statusbar bar;
+CODE:
+       RETVAL = bar->parent_window;
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::TextUI::StatusbarItem  PACKAGE = Irssi::TextUI::StatusbarItem
+#*******************************
+
+Irssi::TextUI::Statusbar
+bar(item)
+       Irssi::TextUI::StatusbarItem item;
+CODE:
+       RETVAL = item->bar;
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::TextUI  PACKAGE = Irssi::TextUI
+#*******************************
Index: src/perl/textui/module.h
===================================================================
--- src/perl/textui/module.h    (revision 4172)
+++ src/perl/textui/module.h    (working copy)
@@ -7,10 +7,11 @@
 #include "textbuffer.h"
 #include "textbuffer-view.h"
 
-typedef MAIN_WINDOW_REC *Irssi__TextUI__MainWindow;
+/* typedef MAIN_WINDOW_REC *Irssi__TextUI__MainWindow; */
 typedef TEXT_BUFFER_REC *Irssi__TextUI__TextBuffer;
 typedef TEXT_BUFFER_VIEW_REC *Irssi__TextUI__TextBufferView;
 typedef LINE_REC *Irssi__TextUI__Line;
 typedef LINE_CACHE_REC *Irssi__TextUI__LineCache;
 typedef LINE_INFO_REC *Irssi__TextUI__LineInfo;
+typedef STATUSBAR_REC *Irssi__TextUI__Statusbar;
 typedef SBAR_ITEM_REC *Irssi__TextUI__StatusbarItem;
Index: src/perl/perl-common.c
===================================================================
--- src/perl/perl-common.c      (revision 4172)
+++ src/perl/perl-common.c      (working copy)
@@ -54,6 +54,9 @@
 
 static GHashTable *iobject_stashes, *plain_stashes;
 static GSList *use_protocols;
+#if xyzzy
+static GHashTable *plain_cache;
+#endif
 
 /* returns the package who called us */
 const char *perl_get_package(void)
@@ -148,13 +151,27 @@
         PERL_OBJECT_FUNC fill_func;
        HV *hv;
 
-       fill_func = g_hash_table_lookup(plain_stashes, stash);
+#if xyzzy
+       hv = g_hash_table_lookup(plain_cache, object);
+        if (hv) {
+               /* increase reference count here */
+               SvREFCNT_inc(hv);
+               return hv;
+       } else {
+#endif
+               fill_func = g_hash_table_lookup(plain_stashes, stash);
 
-       hv = newHV();
-       hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
-       if (fill_func != NULL)
-               fill_func(hv, object);
-       return sv_bless(newRV_noinc((SV*)hv), gv_stashpv((char *)stash, 1));
+               hv = newHV();
+#if xyzzy
+               g_hash_table_insert(plain_cache, object, hv);
+#endif
+               hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
+               if (fill_func != NULL)
+                       fill_func(hv, object);
+               return sv_bless(newRV_noinc((SV*)hv), gv_stashpv((char *)stash, 
1));
+#if xyzzy
+       }
+#endif
 }
 
 int irssi_is_ref_object(SV *o)
@@ -644,6 +661,22 @@
        return FALSE;
 }
 
+static void free_iobject_cache(void *key, HV *hv)
+{
+       /* decrement refcount on hv */
+       SvREFCNT_dec(hv);
+}
+
+#if xyzzy
+static void free_plain_cache(void *key, HV *hv)
+{
+       if (hv) {
+               /* decrement refcount on hv */
+               SvREFCNT_dec(hv);
+       }
+}
+#endif
+
 static void perl_unregister_protocol(CHAT_PROTOCOL_REC *rec)
 {
        GSList *item;
@@ -678,6 +711,10 @@
                                        (GCompareFunc) g_direct_equal);
        plain_stashes = g_hash_table_new((GHashFunc) g_str_hash,
                                         (GCompareFunc) g_str_equal);
+#if xyzzy
+       plain_cache = g_hash_table_new((GHashFunc) g_direct_hash,
+                                        (GCompareFunc) g_direct_equal);
+#endif
         irssi_add_plains(core_plains);
 
         use_protocols = NULL;
@@ -697,6 +734,12 @@
        g_hash_table_destroy(plain_stashes);
         plain_stashes = NULL;
 
+#if xyzzy
+        g_hash_table_foreach(plain_cache, (GHFunc) free_plain_cache, NULL);
+       g_hash_table_destroy(plain_cache);
+        plain_cache = NULL;
+#endif
+
        g_slist_foreach(use_protocols, (GFunc) g_free, NULL);
        g_slist_free(use_protocols);
         use_protocols = NULL;
Index: src/perl/ui/typemap
===================================================================
--- src/perl/ui/typemap (revision 4172)
+++ src/perl/ui/typemap (working copy)
@@ -1,5 +1,6 @@
 TYPEMAP
 Irssi::UI::Theme               T_PlainObj
+Irssi::TextUI::MainWindow              T_PlainObj
 Irssi::UI::Window              T_PlainObj
 Irssi::UI::Keyinfo             T_PlainObj
 Irssi::UI::TextDest            T_PlainObj
Index: src/perl/ui/Window.xs
===================================================================
--- src/perl/ui/Window.xs       (revision 4172)
+++ src/perl/ui/Window.xs       (working copy)
@@ -242,6 +242,14 @@
 OUTPUT:
        RETVAL
 
+Irssi::TextUI::MainWindow
+parent_mainwindow(window)
+       Irssi::UI::Window window
+CODE:
+       RETVAL = WINDOW_MAIN(window);
+OUTPUT:
+       RETVAL
+
 #*******************************
 MODULE = Irssi::UI::Window  PACKAGE = Irssi::Windowitem  PREFIX = window_item_
 #*******************************
Index: src/perl/ui/UI.xs
===================================================================
--- src/perl/ui/UI.xs   (revision 4172)
+++ src/perl/ui/UI.xs   (working copy)
@@ -1,4 +1,5 @@
 #include "module.h"
+#include "fe-text/gui-windows.h"
 
 void perl_themes_init(void);
 void perl_themes_deinit(void);
@@ -35,6 +36,9 @@
                hv_store(hv, "active", 6, iobject_bless(window->active), 0);
        if (window->active_server)
                hv_store(hv, "active_server", 13, 
iobject_bless(window->active_server), 0);
+       /*
+       hv_store(hv, "parent", 6, plain_bless(WINDOW_MAIN(window), 
"Irssi::TextUI::MainWindow"), 0);
+       */
 
        hv_store(hv, "servertag", 9, new_pv(window->servertag), 0);
        hv_store(hv, "level", 5, newSViv(window->level), 0);
Index: src/perl/ui/module.h
===================================================================
--- src/perl/ui/module.h        (revision 4172)
+++ src/perl/ui/module.h        (working copy)
@@ -1,6 +1,7 @@
 #include "../common/module.h"
 
 #include "fe-windows.h"
+#include "fe-text/gui-windows.h"
 #include "fe-exec.h"
 #include "formats.h"
 #include "printtext.h"
@@ -8,6 +9,7 @@
 #include "themes.h"
 #include "keyboard.h"
 
+typedef MAIN_WINDOW_REC *Irssi__TextUI__MainWindow;
 typedef WINDOW_REC *Irssi__UI__Window;
 typedef TEXT_DEST_REC *Irssi__UI__TextDest;
 typedef THEME_REC *Irssi__UI__Theme;
Index: src/fe-text/mainwindows.c
===================================================================
--- src/fe-text/mainwindows.c   (revision 4172)
+++ src/fe-text/mainwindows.c   (working copy)
@@ -837,26 +837,54 @@
        window_set_active(window);
 }
 
+MAIN_WINDOW_REC *mainwindow_up(MAIN_WINDOW_REC *mainwin, int wrap)
+{
+       MAIN_WINDOW_REC *rec;
+
+       rec = mainwindows_find_upper(mainwin->first_line);
+       if (rec == NULL && wrap)
+               rec = mainwindows_find_upper(term_height);
+       return rec;
+}
+
 /* SYNTAX: WINDOW UP */
 static void cmd_window_up(void)
 {
        MAIN_WINDOW_REC *rec;
 
+#if 1
+       rec = mainwindow_up(active_mainwin, TRUE);
+#else
        rec = mainwindows_find_upper(active_mainwin->first_line);
        if (rec == NULL)
                rec = mainwindows_find_upper(term_height);
+#endif
        if (rec != NULL)
                window_set_active(rec->active);
 }
 
+MAIN_WINDOW_REC *mainwindow_down(MAIN_WINDOW_REC *mainwin, int wrap)
+{
+       MAIN_WINDOW_REC *rec;
+
+       rec = mainwindows_find_lower(mainwin->last_line);
+       if (rec == NULL && wrap)
+               rec = mainwindows_find_lower(-1);
+       return rec;
+}
+
 /* SYNTAX: WINDOW DOWN */
 static void cmd_window_down(void)
 {
        MAIN_WINDOW_REC *rec;
 
+#if 1
+       rec = mainwindow_down(active_mainwin, TRUE);
+#else
        rec = mainwindows_find_lower(active_mainwin->last_line);
        if (rec == NULL)
                rec = mainwindows_find_lower(-1);
+#endif
        if (rec != NULL)
                window_set_active(rec->active);
 }
@@ -866,7 +894,7 @@
         (WINDOW_GUI(window)->sticky && \
          WINDOW_MAIN(window) == (sticky_parent)))
 
-static int window_refnum_left(int refnum, int wrap)
+int window_refnum_left(int refnum, int wrap)
 {
         MAIN_WINDOW_REC *find_sticky;
        WINDOW_REC *window;
@@ -888,7 +916,7 @@
         return refnum;
 }
 
-static int window_refnum_right(int refnum, int wrap)
+int window_refnum_right(int refnum, int wrap)
 {
         MAIN_WINDOW_REC *find_sticky;
        WINDOW_REC *window;
Index: src/fe-text/mainwindows.h
===================================================================
--- src/fe-text/mainwindows.h   (revision 4172)
+++ src/fe-text/mainwindows.h   (working copy)
@@ -57,4 +57,9 @@
 
 GSList *mainwindows_get_sorted(int reverse);
 
+MAIN_WINDOW_REC *mainwindow_up(MAIN_WINDOW_REC *mainwin, int wrap);
+MAIN_WINDOW_REC *mainwindow_down(MAIN_WINDOW_REC *mainwin, int wrap);
+int window_refnum_left(int refnum, int wrap);
+int window_refnum_right(int refnum, int wrap);
+
 #endif
Index: docs/perl.txt
===================================================================
--- docs/perl.txt       (revision 4172)
+++ docs/perl.txt       (working copy)
@@ -149,7 +149,13 @@
 know where to run the script, or at least later figure out why it
 didn't work :)
 
+ MainWindows
+ -----------
 
+Where the user documentation speaks of "split windows", the corresponding
+perl object is called Irssi::TextUI::MainWindow. The MainWindow may contain
+several Windows but only one is active (shown) at any one time.
+
  Functions that you can use in Irssi's Perl scripts
  --------------------------------------------------
 
@@ -177,8 +183,10 @@
 
 Window active_win() - return active window
 Server active_server() - return server in active window
+MainWindow active_mainwindow() - return active mainwindow
 
 windows() - return list of all windows
+mainwindows() - return list of all split windows
 servers() - return list of all servers
 reconnects() - return list of all server reconnections
 channels() - return list of all channels
@@ -465,6 +473,52 @@
   Return active item's name, or if none is active, window's name
 
 
+TextUI::MainWindow->{}
+  active - the Irssi::UI::Window that is active inside
+  sticky_windows - number of sticky windows
+  first_line, last_line - first/last line used by this window (0..x) (includes
+                           statusbars)
+  width, height - width/height of the window (includes statusbars)
+  statusbar_lines_top
+  statusbar_lines_bottom
+  statusbar_lines - top+bottom
+  dirty - This window needs a redraw
+  size_dirty - We'll need to resize the window, but haven't got around
+               doing it just yet.
+
+
+UI::Window
+MainWindow::parent_mainwindow()
+  To get from a Window to the MainWindow that shows it, use this function.
+  It is not a field because that would create a circular data structure,
+  and the Perl interface can't handle that.
+
+(TextUI::Statusbar)
+MainWindow::statusbars()
+  A list of the TextUI::StatusBar objects in the TextUI::MainWindow
+
+
+TextUI::StatusbarItem->{}
+  min_size
+  max_size
+  xpos
+  size
+  window
+
+TextUI::Statusbar
+StatusbarItem::bar()
+  Get the statusbar to which the statusbaritem belongs.
+
+(TextUI::StatusbarItem)
+StatusbarItem::items()
+  Get the TextUI::StatusbarItems in the Statusbar.
+
+TextUI::MainWindow
+Statusbar::parent_window()
+  Get the MainWindow that shows the Statusbar.
+
+
+
  *** Server Connects
 
 Connect->{}


-Olaf.
-- 
___ Olaf 'Rhialto' Seibert      -- You author it, and I'll reader it.
\X/ rhialto/at/xs4all.nl        -- Cetero censeo "authored" delendum esse.

Reply via email to