12 void settitle(string title
);
13 void mvaddch(dchar ch
, uint y
, uint x
, RGBColour fg
= RGBColour(0xffffff), RGBColour bg
= RGBColour(0x000000), bool bold
= false, bool italic
= false, bool underline
= false, bool reverse
= false);
16 final void printext(string text
, uint y
, uint x
, RGBColour fg
= RGBColour(0xffffff), RGBColour bg
= RGBColour(0x000000), bool bold
= false, bool italic
= false, bool underline
= false, bool reverse
= false) {
17 import std
.conv
: dtext
;
18 foreach (ch
; dtext(text
)) {
19 mvaddch(ch
, y
, x
++ /* no wrapping, just go off the edge of the screen */, fg
, bg
, bold
, italic
, underline
, reverse
);
25 class SDL0
: CharGfx0
{
26 import derelict
.sdl2
.image
, derelict
.sdl2
.sdl
, derelict
.sdl2
.ttf
, derelict
.sdl2
.mixer
;
28 SDL_Renderer
*renderer
;
29 SDL_Window
*sdl_window
;
31 SDL_Texture
*[] tileset
;
32 SDL_Texture
*[] tileset_italic
;
33 SDL_Texture
*[] tileset_italicbold
;
34 SDL_Texture
*[] tileset_bold
;
36 private struct CoordChar
{
38 RGBColour fg
= RGBColour(0xffffff), bg
= RGBColour(0x0);
39 bool bold
, italic
, underline
, reverse
= false;
45 enum tile_height
= tile_width
* 2;
47 private void sdlerror() {
48 import std
.string
: fromStringz
;
49 throw new Exception(cast(string
)("Error from SDL. SDL says: " ~ fromStringz(SDL_GetError())));
53 private bool loadfont(string fpath
, ushort height
, ref SDL_Texture
*[] target
) {
54 import std
.string
: toStringz
;
56 TTF_Font
*font
= TTF_OpenFont(toStringz(fpath
), height
);
59 SDL_Color white
= SDL_Color(255, 255, 255, 0);
65 TTF_SetFontKerning(font
, 0);
69 target
= new SDL_Texture
*[65536];
71 foreach (ushort i
; 0 .. 65536) {
72 if (TTF_GlyphIsProvided(font
, i
)) {
74 surface
= TTF_RenderUNICODE_Blended(font
, text
.ptr
, white
);
75 target
[i
] = SDL_CreateTextureFromSurface(renderer
, surface
);
76 SDL_FreeSurface(surface
);
90 version (dynamic_sdl2
) {
92 DerelictSDL2Image
.load();
93 DerelictSDL2TTF
.load();
94 DerelictSDL2Mixer
.load();
97 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_AUDIO
) < 0) {
101 if (TTF_Init() == -1) {
107 int mix_flags = MIX_INIT_FLAC | MIX_INIT_MP3 | MIX_INIT_OGG;
109 if ((Mix_Init(mix_flags) & mix_flags) != mix_flags)
113 if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
116 // no thanks for the formatting, though, you lazy foo!
120 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY
, "2");
122 // Title, bunch of stuff, resolution, borderless fullscreen
123 // SDL_WINDOW_FULLSCREEN actually changes the video mode
124 sdl_window
= SDL_CreateWindow(null, SDL_WINDOWPOS_UNDEFINED
, SDL_WINDOWPOS_UNDEFINED
, min_x
* tile_width
, min_y
* tile_height
, cast(SDL_WindowFlags
)0);
125 if (!sdl_window
) { sdlerror(); }
126 // sdl_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP);
127 renderer
= SDL_CreateRenderer(sdl_window
, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
);
129 if (!loadfont("assets/font/dvsm.ttf", tile_height
, tileset
)) { sdlerror(); }
130 if (!loadfont("assets/font/dvsm-italic.ttf", tile_height
, tileset_italic
)) { sdlerror(); }
131 if (!loadfont("assets/font/dvsm-bolditalic.ttf", tile_height
, tileset_italicbold
)) { sdlerror(); }
132 if (!loadfont("assets/font/dvsm-bold.ttf", tile_height
, tileset_bold
)) { sdlerror(); }
134 screen
= new CoordChar
[][](maxx(), maxy());
139 SDL_SetRenderDrawColor(renderer
, 0, 0, 0, 255);
140 SDL_RenderClear(renderer
);
142 // draw the characters onto it
143 foreach (y
; 0 .. maxy()) {
144 foreach (x
; 0 .. maxx()) {
145 actual_mvaddch(screen
[y
][x
], y
, x
);
150 // Blit it to the screen. No magic happening here, just some x11 (or win32 or cocoa or what-have-you) calls
151 SDL_RenderPresent(renderer
);
156 SDL_GetWindowSize(sdl_window
, &w
, null);
157 return w
/ tile_width
;
162 SDL_GetWindowSize(sdl_window
, null, &h
);
163 return h
/ tile_height
;
167 import std
.string
: fromStringz
;
171 SDL_StartTextInput();
173 if (SDL_WaitEvent(&e
) == 1) {
174 if (e
.type
== SDL_TEXTINPUT
) {
175 //return to!dchar(e.text.text);
176 return e
.text
.text
[0];
177 // gained focus, so we need to redraw. IDK why
178 } else if (e
.type
== SDL_WINDOWEVENT
) {
187 void settitle(string title
) {
188 import std
.string
: toStringz
;
189 SDL_SetWindowTitle(sdl_window
, toStringz(title
));
192 void mvaddch(dchar ch
, uint y
, uint x
, RGBColour fg
= RGBColour(0xffffff), RGBColour bg
= RGBColour(0x000000), bool bold
= false, bool italic
= false, bool underline
= false, bool reverse
= false) {
193 if (y
>= screen
.length
) {
194 screen
.length
= maxy();
196 foreach (ref col
; screen
) {
200 screen
[y
][x
] = CoordChar(ch
, fg
, bg
, bold
, italic
, underline
, reverse
);
203 private void actual_mvaddch(CoordChar character
, uint y
, uint x
) { with (character
) {
210 SDL_Texture
*renderedchar
;
212 if (ch
> wchar.max
) {
213 log("Character %s large, must fit into a wchar (this is an SDL limitation)", ch
);
218 SDL_Texture*[][][] tilesetset = [[tileset, tileset_italic], [tileset_bold, tileset_italicbold]];
219 SDL_Texture*[] tiles = tilesetset[bold][italic];
221 SDL_Texture
*[] tiles
;
224 tiles
= tileset_italicbold
;
226 tiles
= tileset_bold
;
229 tiles
= tileset_italic
;
234 // NULL means there's no glyph so fall back to '?'
235 if (tiles
[ch
] is null) {
236 renderedchar
= tiles
['?'];
238 renderedchar
= tiles
[ch
];
241 // We just draw a square with colour bg, then draw the char on top of
242 // it, but with transparency so we see the bg.
246 tile
.y
= y
* tile_height
;
247 tile
.x
= x
* tile_width
;
249 tile
.h
= tile_height
;
251 // Set the colour to draw with
252 SDL_SetRenderDrawColor(renderer
, bg
.r
, bg
.g
, bg
.b
, 255);
255 SDL_RenderFillRect(renderer
, &tile
);
257 // Colourize the letter itself. The parts that aren't the letter will
258 // also get colourized, but that doesn't matter because they have alpha
260 SDL_SetTextureColorMod(renderedchar
, fg
.r
, fg
.g
, fg
.b
);
262 // And finally, copy everything over to the actual renderer
263 SDL_RenderCopy(renderer
, renderedchar
, null, &tile
);
267 foreach (tilesets
; [tileset
, tileset_bold
, tileset_italic
, tileset_italicbold
]) {
268 foreach (ref texture
; tilesets
) {
269 SDL_DestroyTexture(texture
);
273 SDL_DestroyRenderer(renderer
);
274 SDL_DestroyWindow(sdl_window
);
275 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_AUDIO
);
283 class NCurses0
: CharGfx0
{
284 import deimos
.ncurses
;
286 import core
.stdc
.locale
: setlocale
, LC_ALL
;
288 setlocale(LC_ALL
, "en_US.UTF-8");
294 keypad(stdscr
, true);
298 void close() { endwin(); }
301 static import deimos
.ncurses
;
302 deimos
.ncurses
.refresh();
306 static import deimos
.ncurses
;
307 return deimos
.ncurses
.getch();
310 uint maxx() { return COLS
; }
311 uint maxy() { return LINES
; }
312 void settitle(string title
) {}
314 void mvaddch(dchar ch
, uint y
, uint x
, RGBColour fg
, RGBColour bg
, bool bold
, bool italic
, bool underline
, bool reverse
) {
315 import std
.conv
: text
;
316 import std
.string
: toStringz
;
318 static ushort[ubyte[2]] clrmap
;
319 static ushort maxclrindex
;
321 ubyte[2] clrs
= [get256colour(fg
), get256colour(bg
)];
322 if (clrs
!in clrmap
) {
323 init_pair(maxclrindex
, clrs
[0], clrs
[1]);
324 clrmap
[clrs
] = maxclrindex
++;
328 return (bold ? A_BOLD
: 0) |
/*(italic ? A_ITALIC : 0) | */ (underline ? A_UNDERLINE
: 0) |
(reverse ? A_REVERSE
: 0);
331 attron(attrs() |
COLOR_PAIR(*(clrs
in clrmap
)));
333 mvprintw(y
, x
, toStringz(text(ch
))); // TODO
335 attroff(attrs() |
COLOR_PAIR(*(clrs
in clrmap
)));
342 class BLT0
: CharGfx0
{
343 import BearLibTerminal
;
346 terminal
.set("input.cursor-blink-rate=2147483647");
347 terminal
.set("input.cursor-symbol=0x2588");
349 terminal
.set("font: assets/font/dvsm.ttf, size=12");
350 terminal
.set("default font: assets/font/dvsm.ttf, size=12");
351 terminal
.set("italic font: assets/font/dvsm-italic.ttf, size=12");
352 terminal
.set("bold font: assets/font/dvsm-bold.ttf, size=12");
353 terminal
.set("bolditalic font: assets/font/dvsm-bolditalic.ttf, size=12");
355 // terminal.set("font: use-box-drawing=false, use-block-elements=false");
356 terminal
.set("window.resizeable=true");
358 void close() { terminal
.close(); }
360 void refresh() { terminal
.refresh(); }
362 uint maxx() { return terminal
.state(terminal
.keycode
.width
); }
363 uint maxy() { return terminal
.state(terminal
.keycode
.height
); }
365 void settitle(string title
) {
366 terminal
.setf("window.title=%s", title
);
369 void mvaddch(dchar ch
, uint y
, uint x
, RGBColour fg
, RGBColour bg
, bool bold
, bool italic
, bool underline
, bool reverse
) {
376 terminal
.bkcolour(bg
);
379 // it's not beautiful, and it's unclear there even exists an
380 // idiomatic way. But this is the most performant way, and this
381 // is a function that gets called thousands of times per refresh
382 static immutable string
[2][2] fontab
= [["default", "italic"], ["bold", "bolditalic"]];
383 terminal
.font(fontab
[bold
][italic
]);
385 // TODO implement bold and italic (have to load alternate fonts)
386 terminal
.put(x
, y
, ch
);
390 terminal
.put(x
, y
, '▁');
392 terminal
.put(x
, y
, ' ');
396 terminal
.font("default");
401 dchar[terminal
.keycode
] keycode2char
= [terminal
.keycode
.a
: 'a',
402 terminal
.keycode
.b
: 'b',
403 terminal
.keycode
.c
: 'c',
404 terminal
.keycode
.d
: 'd',
405 terminal
.keycode
.e
: 'e',
406 terminal
.keycode
.f
: 'f',
407 terminal
.keycode
.g
: 'g',
408 terminal
.keycode
.h
: 'h',
409 terminal
.keycode
.i
: 'i',
410 terminal
.keycode
.j
: 'j',
411 terminal
.keycode
.k
: 'k',
412 terminal
.keycode
.l
: 'l',
413 terminal
.keycode
.m
: 'm',
414 terminal
.keycode
.n
: 'n',
415 terminal
.keycode
.o
: 'o',
416 terminal
.keycode
.p
: 'p',
417 terminal
.keycode
.q
: 'q',
418 terminal
.keycode
.r
: 'r',
419 terminal
.keycode
.s
: 's',
420 terminal
.keycode
.t
: 't',
421 terminal
.keycode
.u
: 'u',
422 terminal
.keycode
.v
: 'v',
423 terminal
.keycode
.w
: 'w',
424 terminal
.keycode
.x
: 'x',
425 terminal
.keycode
.y
: 'y',
426 terminal
.keycode
.z
: 'z',
427 terminal
.keycode
.KP_1
: '1',
428 terminal
.keycode
.KP_2
: '2',
429 terminal
.keycode
.KP_3
: '3',
430 terminal
.keycode
.KP_4
: '4',
431 terminal
.keycode
.KP_5
: '5',
432 terminal
.keycode
.KP_6
: '6',
433 terminal
.keycode
.KP_7
: '7',
434 terminal
.keycode
.KP_8
: '8',
435 terminal
.keycode
.KP_9
: '9',
436 terminal
.keycode
.KP_0
: '0',
437 terminal
.keycode
.enter: '\n',
438 terminal
.keycode
.escape
: '\033',
439 terminal
.keycode
.backspace
: '\b',
440 terminal
.keycode
.tab
: '\t',
441 terminal
.keycode
.space
: ' ',
442 terminal
.keycode
.minus
: '-',
443 terminal
.keycode
.equals
: '=',
444 terminal
.keycode
.lbracket
: '[',
445 terminal
.keycode
.rbracket
: ']',
446 terminal
.keycode
.backslash
: '\\',
447 terminal
.keycode
.semicolon
: ';',
448 terminal
.keycode
.apostrophe
: '\'',
449 terminal
.keycode
.grave
: '`',
450 terminal
.keycode
.comma
: ',',
451 terminal
.keycode
.period
: '.',
452 terminal
.keycode
.slash
: '/',
465 pause = 0x48 /* Pause/Break */,
472 right = 0x4F /* Right arrow */,
473 left = 0x50 /* Left arrow */,
474 down = 0x51 /* Down arrow */,
475 up = 0x52 /* Up arrow */,
477 terminal
.keycode
.KP_divide
: '/',
478 terminal
.keycode
.KP_multiply
: '*',
479 terminal
.keycode
.KP_minus
: '-',
480 terminal
.keycode
.KP_plus
: '+',
481 terminal
.keycode
.KP_enter
: '\n',
482 terminal
.keycode
.KP_1
: '1',
483 terminal
.keycode
.KP_2
: '2',
484 terminal
.keycode
.KP_3
: '3',
485 terminal
.keycode
.KP_4
: '4',
486 terminal
.keycode
.KP_5
: '5',
487 terminal
.keycode
.KP_6
: '6',
488 terminal
.keycode
.KP_7
: '7',
489 terminal
.keycode
.KP_8
: '8',
490 terminal
.keycode
.KP_9
: '9',
491 terminal
.keycode
.KP_0
: '0',
492 terminal
.keycode
.KP_period
: '.',
498 mouse_left = 0x80 /* Buttons */,
503 mouse_move = 0x85 /* Movement event */,
504 mouse_scroll = 0x86 /* Mouse scroll event */,
505 mouse_x = 0x87 /* Cusor position in cells */,
507 mouse_pixel_x = 0x89 /* Cursor position in pixels */,
508 mouse_pixel_y = 0x8A,
509 mouse_wheel = 0x8B /* Scroll direction and amount */,
510 mouse_clicks = 0x8C /* Number of consecutive clicks */,
514 while ((k
= terminal
.read()) !in keycode2char
) {}
515 return keycode2char
[k
];