Skip to content

Commit ebebe2d

Browse files
committed
Add Curses::Screen (fixes #55)
1 parent 2539b97 commit ebebe2d

File tree

3 files changed

+156
-7
lines changed

3 files changed

+156
-7
lines changed

ext/curses/curses.c

+146-6
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ rb_obj2chtype_inline(VALUE x)
9898

9999
static VALUE mCurses;
100100
static VALUE mKey;
101+
static VALUE cScreen;
101102
static VALUE cWindow;
102103
static VALUE cPad;
103104
#ifdef USE_MOUSE
@@ -220,6 +221,7 @@ check_curses_error(int error)
220221

221222
struct windata {
222223
WINDOW *window;
224+
int is_stdscr;
223225
};
224226

225227
static VALUE window_attroff(VALUE obj, VALUE attrs);
@@ -242,7 +244,7 @@ static void
242244
window_free(void *p)
243245
{
244246
struct windata *winp = p;
245-
if (winp->window && winp->window != stdscr) delwin(winp->window);
247+
if (winp->window && !winp->is_stdscr) delwin(winp->window);
246248
winp->window = 0;
247249
xfree(winp);
248250
}
@@ -263,7 +265,7 @@ static const rb_data_type_t windata_type = {
263265
};
264266

265267
static VALUE
266-
prep_window(VALUE class, WINDOW *window)
268+
prep_window(VALUE class, WINDOW *window, int is_stdscr)
267269
{
268270
VALUE obj;
269271
struct windata *winp;
@@ -275,6 +277,7 @@ prep_window(VALUE class, WINDOW *window)
275277
obj = rb_obj_alloc(class);
276278
TypedData_Get_Struct(obj, struct windata, &windata_type, winp);
277279
winp->window = window;
280+
winp->is_stdscr = is_stdscr;
278281

279282
return obj;
280283
}
@@ -311,7 +314,7 @@ curses_init_screen(VALUE self)
311314
}
312315
rb_set_end_proc(curses_finalize, 0);
313316
clear();
314-
rb_stdscr = prep_window(cWindow, stdscr);
317+
rb_stdscr = prep_window(cWindow, stdscr, 1);
315318
return rb_stdscr;
316319
}
317320

@@ -1683,6 +1686,117 @@ curses_reset_prog_mode(VALUE obj)
16831686
#define curses_reset_prog_mode rb_f_notimplement
16841687
#endif
16851688

1689+
/*-------------------------- class Screen --------------------------*/
1690+
1691+
struct screendata {
1692+
SCREEN *screen;
1693+
VALUE stdscr;
1694+
};
1695+
1696+
NORETURN(static void no_screen(void));
1697+
static void
1698+
no_screen(void)
1699+
{
1700+
rb_raise(rb_eRuntimeError, "no screen");
1701+
}
1702+
1703+
#define GetSCREEN(obj, screenp) do {\
1704+
TypedData_Get_Struct((obj), struct screendata, &screendata_type, (screenp));\
1705+
if ((screenp)->screen == 0) no_screen();\
1706+
} while (0)
1707+
1708+
static void
1709+
screen_gc_mark(void *p)
1710+
{
1711+
struct screendata *screenp = p;
1712+
1713+
rb_gc_mark(screenp->stdscr);
1714+
}
1715+
1716+
static void
1717+
screen_free(void *p)
1718+
{
1719+
struct screendata *screenp = p;
1720+
if (screenp->screen) delscreen(screenp->screen);
1721+
screenp->screen = 0;
1722+
xfree(screenp);
1723+
}
1724+
1725+
static size_t
1726+
screen_memsize(const void *p)
1727+
{
1728+
const struct screendata *screenp = p;
1729+
size_t size = sizeof(*screenp);
1730+
if (!screenp) return 0;
1731+
if (screenp->screen) size += CURSES_SIZEOF_SCREEN;
1732+
return size;
1733+
}
1734+
1735+
static const rb_data_type_t screendata_type = {
1736+
"screendata",
1737+
{screen_gc_mark, screen_free, screen_memsize,}
1738+
};
1739+
1740+
/* returns a Curses::Screen object */
1741+
static VALUE
1742+
screen_s_allocate(VALUE class)
1743+
{
1744+
struct screendata *screenp;
1745+
1746+
return TypedData_Make_Struct(class, struct screendata, &screendata_type, screenp);
1747+
}
1748+
1749+
/*
1750+
* Document-method: Curses::Screen.new
1751+
* call-seq: new(outf, inf, type=nil)
1752+
*
1753+
* Construct a new Curses::Screen.
1754+
*/
1755+
static VALUE
1756+
screen_initialize(int argc, VALUE *argv, VALUE obj)
1757+
{
1758+
VALUE outf, inf, type;
1759+
struct screendata *screenp;
1760+
rb_io_t *outfptr, *infptr;
1761+
1762+
rb_scan_args(argc, argv, "21", &outf, &inf, &type);
1763+
TypedData_Get_Struct(obj, struct screendata, &screendata_type, screenp);
1764+
if (screenp->screen) delscreen(screenp->screen);
1765+
Check_Type(outf, T_FILE);
1766+
RB_IO_POINTER(outf, outfptr);
1767+
rb_io_check_writable(outfptr);
1768+
Check_Type(inf, T_FILE);
1769+
RB_IO_POINTER(inf, infptr);
1770+
rb_io_check_readable(infptr);
1771+
screenp->screen = newterm(NIL_P(type) ? NULL : StringValueCStr(type),
1772+
rb_io_stdio_file(outfptr),
1773+
rb_io_stdio_file(infptr));
1774+
screenp->stdscr = Qnil;
1775+
1776+
return obj;
1777+
}
1778+
1779+
/*
1780+
* Document-method: Curses::Screen.set_term
1781+
* call-seq: set_term
1782+
*
1783+
* Set the current terminal.
1784+
*/
1785+
static VALUE
1786+
screen_set_term(VALUE obj)
1787+
{
1788+
struct screendata *screenp;
1789+
1790+
GetSCREEN(obj, screenp);
1791+
set_term(screenp->screen);
1792+
if (NIL_P(screenp->stdscr)) {
1793+
screenp->stdscr = prep_window(cWindow, stdscr, 1);
1794+
}
1795+
rb_stdscr = screenp->stdscr;
1796+
1797+
return Qnil;
1798+
}
1799+
16861800
/*-------------------------- class Window --------------------------*/
16871801

16881802
/* returns a Curses::Window object */
@@ -1743,7 +1857,7 @@ window_subwin(VALUE obj, VALUE height, VALUE width, VALUE top, VALUE left)
17431857
l = NUM2INT(left);
17441858
GetWINDOW(obj, winp);
17451859
window = subwin(winp->window, h, w, t, l);
1746-
win = prep_window(rb_obj_class(obj), window);
1860+
win = prep_window(rb_obj_class(obj), window, 0);
17471861

17481862
return win;
17491863
}
@@ -1772,7 +1886,7 @@ window_derwin(VALUE obj, VALUE height, VALUE width, VALUE top, VALUE left)
17721886
l = NUM2INT(left);
17731887
GetWINDOW(obj, winp);
17741888
window = derwin(winp->window, h, w, t, l);
1775-
win = prep_window(rb_obj_class(obj), window);
1889+
win = prep_window(rb_obj_class(obj), window, 0);
17761890

17771891
return win;
17781892
}
@@ -3011,7 +3125,7 @@ pad_subpad(VALUE obj, VALUE height, VALUE width, VALUE begin_x, VALUE begin_y)
30113125
y = NUM2INT(begin_y);
30123126
GetWINDOW(obj, padp);
30133127
sub_pad = subpad(padp->window, h, w, x, y);
3014-
pad = prep_window(rb_obj_class(obj), sub_pad);
3128+
pad = prep_window(rb_obj_class(obj), sub_pad, 0);
30153129

30163130
return pad;
30173131
}
@@ -4998,6 +5112,32 @@ Init_curses(void)
49985112
rb_define_const(mCurses, "VERSION", version);
49995113
}
50005114

5115+
/*
5116+
* Document-class: Curses::Screen
5117+
*
5118+
* == Description
5119+
*
5120+
* A Screen represents a terminal.
5121+
* A program that outputs to more than one terminal should create a Screen
5122+
* for each terminal instead of calling Curses.init_screen.
5123+
*
5124+
* == Usage
5125+
*
5126+
* require "curses"
5127+
*
5128+
* screen = Screen.new(STDOUT, STDIN, "xterm")
5129+
* screen.set_term
5130+
*
5131+
* Curses.addstr("Hit any key")
5132+
* Curses.refresh
5133+
* Curses.getch
5134+
* Curses.close_screen
5135+
*/
5136+
cScreen = rb_define_class_under(mCurses, "Screen", rb_cObject);
5137+
rb_define_alloc_func(cScreen, screen_s_allocate);
5138+
rb_define_method(cScreen, "initialize", screen_initialize, -1);
5139+
rb_define_method(cScreen, "set_term", screen_set_term, 0);
5140+
50015141
/*
50025142
* Document-class: Curses::Window
50035143
*

ext/curses/extconf.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def exec_command(cmd)
221221
have_func("form_driver_w")
222222
end
223223

224-
["WINDOW", "MEVENT", "ITEM", "MENU", "FIELD", "FORM"].each do |type|
224+
["WINDOW", "MEVENT", "ITEM", "MENU", "FIELD", "FORM", "SCREEN"].each do |type|
225225
checking_for("sizeof(#{type}) is available") {
226226
if try_compile(<<EOF, Shellwords.join($defs))
227227
#if defined(HAVE_PDCURSES_H)

sample/screen.rb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
require "curses"
2+
3+
screen = Curses::Screen.new(STDOUT, STDIN, "xterm")
4+
screen.set_term
5+
6+
Curses.addstr("Hit any key")
7+
Curses.refresh
8+
Curses.getch
9+
Curses.close_screen

0 commit comments

Comments
 (0)