cosmetix
[xreader.git] / xreaderfmt.d
blobe115d0daed885bed2d45b75af0bf814a80f3ae81
1 /* Written by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module xreaderfmt;
19 import core.time;
21 import std.concurrency;
23 import iv.strex;
25 import arsd.simpledisplay;
26 import arsd.image;
28 import iv.cmdcon;
29 import iv.nanovg;
30 import iv.nanovg.oui.blendish;
31 import iv.vfs;
32 import iv.vfs.io;
34 import booktext;
36 import xreadercfg;
37 import xlayouter;
40 // ////////////////////////////////////////////////////////////////////////// //
41 BookText loadBook (string fname) {
42 __gshared bool eliteShipsLoaded = false;
44 import std.path;
45 //import core.memory : GC;
46 fname = fname.expandTilde.absolutePath;
47 //conwriteln("loading '", fname, "'...");
48 //GC.disable();
49 auto stt = MonoTime.currTime;
50 auto book = new BookText(fname);
51 //GC.enable();
52 //GC.collect();
53 { conwriteln("loaded: '", fname, "' in ", (MonoTime.currTime-stt).total!"msecs", " milliseconds"); }
55 string[] lines;
56 string[] comments;
57 try {
58 foreach (string line; VFile(buildPath(RcDir, ".lastfile")).byLineCopy) {
59 if (line != fname) {
60 lines ~= line;
61 } else {
62 while (lines.length && lines[$-1].isComment) {
63 comments ~= lines[$-1];
64 lines = lines[0..$-1];
68 } catch (Exception) {}
69 try { import std.file; mkdirRecurse(RcDir); } catch (Exception) {}
70 auto fo = VFile(buildPath(RcDir, ".lastfile"), "w");
71 foreach (string s; lines) fo.writeln(s);
72 foreach_reverse (string s; comments) fo.writeln(s);
73 fo.writeln(fname);
75 stateFileName = buildPath(RcDir, fname.baseName~".rc");
77 if (!eliteShipsLoaded) {
78 loadEliteShips();
79 eliteShipsLoaded = true;
82 return book;
86 // ////////////////////////////////////////////////////////////////////////// //
87 private struct ImageInfo {
88 enum MaxIdleFrames = 5;
89 int iid;
90 TrueColorImage img;
91 int framesNotUsed;
94 private __gshared ImageInfo[] nvgImageIds;
97 void releaseImages (NVGContext vg) {
98 if (nvgImageIds.length) {
99 foreach (ref ImageInfo nfo; nvgImageIds) {
100 if (nfo.iid >= 0) {
101 if (++nfo.framesNotUsed > ImageInfo.MaxIdleFrames) {
102 //conwriteln("freeing image with id ", nfo.iid);
103 vg.deleteImage(nfo.iid);
104 nfo.iid = -1;
105 nfo.img = null;
113 private int registerImage (NVGContext vg, TrueColorImage img) {
114 if (vg is null || img is null) return -1;
115 uint freeIdx = uint.max;
116 foreach (immutable idx, ref ImageInfo nfo; nvgImageIds) {
117 if (nfo.img is img) {
118 assert(nfo.iid >= 0);
119 nfo.framesNotUsed = 0;
120 return nfo.iid;
122 if (freeIdx != uint.max && nfo.iid < 0) freeIdx = cast(uint)idx;
124 int iid = vg.createImageFromMemoryImage(img); //vg.createImageRGBA(img.width, img.height, img.imageData.bytes[], NVGImageFlags.None);
125 if (iid < 0) return -1;
126 nvgImageIds ~= ImageInfo(iid, img, 0);
127 return iid;
131 class BookImage : LayObject {
132 BookText book;
133 uint imageidx;
134 TrueColorImage img;
135 this (BookText abook, uint aidx) { book = abook; imageidx = aidx; img = book.images[aidx].img; }
136 override int width () { return img.width; }
137 override int spacewidth () { return 0; }
138 override int height () { return img.height; }
139 override int ascent () { return img.height; }
140 override int descent () { return 0; }
141 override bool canbreak () { return true; }
142 override bool spaced () { return false; }
143 override void draw (NVGContext vg, float x, float y) {
144 y -= img.height;
145 vg.save();
146 scope(exit) vg.restore();
147 vg.beginPath();
148 int iid = vg.registerImage(img);
149 if (iid < 0) return;
150 vg.fillPaint(vg.imagePattern(x, y, img.width, img.height, 0, iid));
151 //vg.globalAlpha(0.5);
152 vg.rect(x, y, img.width, img.height);
153 vg.fill();
158 // ////////////////////////////////////////////////////////////////////////// //
159 class BookMetadata {
160 static struct Anc {
161 dstring name;
162 uint wordidx; // first word
164 Anc[] sections;
165 Anc[uint] hrefs; // `name` is dest
166 Anc[] ids;
170 // ////////////////////////////////////////////////////////////////////////// //
171 private void fixFont (LayText lay) {
172 if (lay.fontStyle.italic) {
173 if (lay.fontStyle.bold) lay.fontStyle.fontface = lay.fontFaceId("textz");
174 else lay.fontStyle.fontface = lay.fontFaceId("texti");
175 } else if (lay.fontStyle.bold) {
176 if (lay.fontStyle.italic) lay.fontStyle.fontface = lay.fontFaceId("textz");
177 else lay.fontStyle.fontface = lay.fontFaceId("textb");
178 } else {
179 lay.fontStyle.fontface = lay.fontFaceId("text");
183 private void setNormalStyle (LayText lay) {
184 lay.fontStyle.resetAttrs;
185 lay.fontStyle.fontsize = fsizeText;
186 lay.fontStyle.color = colorText.asUint;
187 lay.lineStyle.leftpad = 0;
188 lay.lineStyle.rightpad = 0;
189 lay.lineStyle.paraIndent = 3;
190 if (optJustify) lay.lineStyle.setJustify; else lay.lineStyle.setLeft;
191 fixFont(lay);
194 private void setTitleStyle (LayText lay) {
195 lay.fontStyle.resetAttrs;
196 lay.fontStyle.bold = true;
197 lay.fontStyle.fontsize = fsizeText+4;
198 lay.fontStyle.color = colorText.asUint;
199 lay.lineStyle.leftpad = 0;
200 lay.lineStyle.rightpad = 0;
201 lay.lineStyle.paraIndent = 0;
202 lay.lineStyle.setCenter;
203 fixFont(lay);
206 private void setSubtitleStyle (LayText lay) {
207 lay.fontStyle.resetAttrs;
208 lay.fontStyle.bold = true;
209 lay.fontStyle.fontsize = fsizeText+2;
210 lay.fontStyle.color = colorText.asUint;
211 lay.lineStyle.leftpad = 0;
212 lay.lineStyle.rightpad = 0;
213 lay.lineStyle.paraIndent = 0;
214 lay.lineStyle.setCenter;
215 fixFont(lay);
218 private void setCiteStyle (LayText lay) {
219 lay.fontStyle.resetAttrs;
220 lay.fontStyle.italic = true;
221 lay.fontStyle.fontsize = fsizeText;
222 lay.fontStyle.color = colorText.asUint;
223 lay.lineStyle.leftpad = fsizeText*3;
224 lay.lineStyle.rightpad = fsizeText*3;
225 lay.lineStyle.paraIndent = 3;
226 if (optJustify) lay.lineStyle.setJustify; else lay.lineStyle.setLeft;
227 fixFont(lay);
230 private void setEpigraphStyle (LayText lay) {
231 lay.fontStyle.resetAttrs;
232 lay.fontStyle.italic = true;
233 lay.fontStyle.fontsize = fsizeText;
234 lay.fontStyle.color = colorText.asUint;
235 lay.lineStyle.leftpad = lay.width/3;
236 lay.lineStyle.rightpad = 0;
237 lay.lineStyle.paraIndent = 0;
238 lay.lineStyle.setRight;
239 fixFont(lay);
242 private void setPoemStyle (LayText lay) {
243 lay.fontStyle.resetAttrs;
244 lay.fontStyle.italic = true;
245 lay.fontStyle.fontsize = fsizeText;
246 lay.fontStyle.color = colorText.asUint;
247 lay.lineStyle.leftpad = fsizeText*3;
248 lay.lineStyle.rightpad = 0;
249 lay.lineStyle.paraIndent = 0;
250 lay.lineStyle.setLeft;
251 fixFont(lay);
254 private void setCodeStyle (LayText lay) {
255 lay.fontStyle.resetAttrs;
256 lay.fontStyle.italic = false;
257 lay.fontStyle.fontsize = fsizeText;
258 lay.fontStyle.color = colorText.asUint;
259 lay.lineStyle.leftpad = fsizeText*3;
260 lay.lineStyle.rightpad = fsizeText*3;
261 lay.lineStyle.paraIndent = 0;
262 lay.lineStyle.setLeft;
263 lay.fontStyle.fontface = lay.fontFaceId("mono");
267 // ////////////////////////////////////////////////////////////////////////// //
268 public BookMetadata formatBook (BookText book, LayText lay) {
269 assert(book !is null);
271 void delegate (Tag ct, int boldc=0, int italicc=0, int underc=0) putTagContentsFwd;
272 void delegate (Tag ct, bool setstyle=true) putEpigraphFwd;
274 BookMetadata meta = new BookMetadata();
276 void dumpTree (Tag ct) {
277 while (ct !is null) {
278 conwriteln("tag: ", ct.name);
279 ct = ct.parent;
283 void badTag (Tag tag, string msg=null) {
284 import std.string : format, indexOf;
285 assert(tag !is null);
286 dumpTree(tag);
287 if (msg.length == 0) msg = "invalid tag: '%s'";
288 if (msg.indexOf("%s") >= 0) {
289 throw new Exception(msg.format(tag.name));
290 } else {
291 throw new Exception(msg);
295 void saveTagId (Tag tag) {
296 if (tag.id.length) {
297 import std.conv : to;
298 meta.ids ~= BookMetadata.Anc(tag.id.to!dstring, lay.nextWordIndex);
302 void putImage (Tag tag) {
303 if (tag is null || tag.href.length < 2 || tag.href[0] != '#') return;
304 string iid = tag.href[1..$];
305 //conwriteln("searching for image with href '", iid, "' (", book.images.length, ")");
306 foreach (immutable idx, ref BookText.Image img; book.images) {
307 if (iid == img.id) {
308 //conwriteln("image '", img.id, "' found");
309 lay.putObject(new BookImage(book, cast(uint)idx));
310 return;
315 void putParaContentInternal (Tag ct, int boldc=0, int italicc=0, int underc=0) {
316 if (ct is null) return;
317 auto c = lay.fontStyle.color;
318 scope(exit) lay.fontStyle.color = c;
319 bool popStyles = false;
320 bool doFixFont = true;
321 scope(exit) {
322 if (popStyles) lay.popStyles;
323 fixFont(lay);
325 saveTagId(ct);
326 switch (ct.name) {
327 case "strong": ++boldc; lay.fontStyle.bold = true; break;
328 case "emphasis": ++italicc; lay.fontStyle.italic = true; break;
329 case "image": putImage(ct); return;
330 case "style": return;
331 case "a":
332 if (ct.href.length) {
333 import std.conv : to;
334 //conwriteln("href found: '", ct.href, "' (", lay.nextWordIndex, ")");
335 meta.hrefs[lay.nextWordIndex] = BookMetadata.Anc(ct.href.to!dstring, lay.nextWordIndex);
336 ++underc;
337 lay.fontStyle.href = true;
338 lay.fontStyle.underline = true;
339 lay.fontStyle.color = colorTextHref.asUint;
341 break;
342 case "sub": break; // some idiotic files has this
343 case "sup": break; // some idiotic files has this
344 case "p":
345 putTagContentsFwd(ct, boldc, italicc, underc);
346 fixFont(lay);
347 return;
348 case "br":
349 //lay.endLine();
350 lay.endPara();
351 return;
352 case "code":
353 lay.pushStyles();
354 popStyles = true;
355 setCodeStyle(lay);
356 doFixFont = false;
357 break;
358 case "site":
359 case "annotation":
360 lay.pushStyles();
361 popStyles = true;
362 ++boldc;
363 lay.fontStyle.bold = true;
364 lay.lineStyle.setCenter;
365 break;
366 case "": lay.put(ct.text); return;
367 default: badTag(ct);
369 if (doFixFont) fixFont(lay);
370 foreach (Tag tag; ct.children) putParaContentInternal(tag, boldc, italicc, underc);
371 switch (ct.name) {
372 case "strong": case "site": case "annotation": if (--boldc == 0) lay.fontStyle.bold = false; break;
373 case "emphasis": if (--italicc == 0) lay.fontStyle.italic = false; break;
374 case "a": if (--underc == 0) lay.fontStyle.underline = false; lay.fontStyle.href = false; break;
375 default:
379 bool onlyImagePara (Tag ct) {
380 if (ct is null) return false;
381 int count;
382 foreach (Tag tag; ct.children) {
383 if (tag.name.length == 0) {
384 // text
385 if (tag.text.xstrip.length != 0) return false;
386 continue;
388 if (count != 0 || tag.name != "image") return false;
389 ++count;
391 return (count == 1);
394 void putTagContents/*(bool xdump=false)*/ (Tag ct, int boldc=0, int italicc=0, int underc=0) {
395 if (ct is null) return;
396 saveTagId(ct);
397 if (onlyImagePara(ct)) {
398 lay.pushStyles();
399 scope(exit) lay.popStyles;
400 lay.lineStyle.setCenter;
401 foreach (Tag tag; ct.children) putParaContentInternal(tag, boldc, italicc, underc);
402 } else {
403 //if (ct.name == "subtitle") conwriteln("text: [", ct.children[0].text, "]");
404 foreach (Tag tag; ct.children) {
405 //static if (xdump) conwriteln("text: [", tag.text, "]");
406 putParaContentInternal(tag, boldc, italicc, underc);
409 lay.endPara();
412 putTagContentsFwd = &putTagContents;
414 void delegate (Tag ct) putPoemx;
415 void putParas (Tag ct) {
416 saveTagId(ct);
417 foreach (Tag tag; ct.children) {
418 if (tag.name.length == 0) continue;
419 if (tag.name == "p") putTagContents(tag);
420 else if (tag.name == "empty-line") lay.endLine();
421 else if (tag.name == "br") lay.endPara();
422 else if (tag.name == "text-author") {}
423 else if (tag.name == "strong") putTagContents(tag, 1);
424 else if (tag.name == "poem") { putPoemx(tag); lay.put(LayText.EndParaCh); }
425 else if (tag.name == "subtitle") {
426 //FIXME:??? somehow this is invisible
427 lay.pushStyles();
428 scope(exit) lay.popStyles;
429 setSubtitleStyle(lay);
430 if (tag.text.length) { putParaContentInternal(tag); lay.endPara(); } else putTagContents(tag);
432 else if (tag.name == "sub" || tag.name == "sup") putTagContents(tag); // some idiotic files has this
433 else if (tag.name == "site" || tag.name == "annotation") putParaContentInternal(tag); // some idiotic files has this
434 else if (tag.name == "epigraph") {
435 lay.pushStyles();
436 scope(exit) lay.popStyles;
437 //auto olpad = lay.lineStyle.leftpad;
438 auto orpad = lay.lineStyle.rightpad;
439 setEpigraphStyle(lay);
440 //lay.lineStyle.leftpad = lay.lineStyle.leftpad+olpad;
441 lay.lineStyle.rightpad = lay.lineStyle.rightpad+orpad;
442 putEpigraphFwd(tag, false);
443 } else badTag(tag);
447 void putAuthor (Tag ct) {
448 saveTagId(ct);
449 foreach (Tag tag; ct.children) {
450 if (tag.name.length == 0) continue;
451 if (tag.name == "text-author") {
452 putTagContents(tag);
453 lay.endPara();
458 void putCite (Tag ct) {
459 lay.pushStyles();
460 scope(exit) lay.popStyles;
461 setCiteStyle(lay);
462 putParas(ct);
463 putAuthor(ct);
466 void putEpigraph (Tag ct, bool setstyle=true) {
467 if (setstyle) lay.pushStyles();
468 scope(exit) if (setstyle) lay.popStyles;
469 if (setstyle) setEpigraphStyle(lay);
470 putParas(ct);
471 putAuthor(ct);
474 putEpigraphFwd = &putEpigraph;
476 void putStanza (Tag ct) {
477 saveTagId(ct);
478 foreach (Tag tag; ct.children) {
479 if (tag.name.length == 0) continue;
480 if (tag.name == "text-author") continue;
481 if (tag.name == "title") badTag(tag, "titles in stanzas are not supported yet");
482 if (tag.name == "subtitle") badTag(tag, "subtitles in stanzas are not supported yet");
483 if (tag.name == "epigraph") badTag(tag, "epigraphs in poems are not supported yet");
484 if (tag.name == "date") continue;
485 if (tag.name == "v") { putTagContents(tag); continue; }
486 badTag(tag);
490 void putPoem (Tag ct) {
491 saveTagId(ct);
492 lay.pushStyles();
493 scope(exit) lay.popStyles;
494 setPoemStyle(lay);
495 // put epigraph (not yet)
496 // put title and subtitle
497 foreach (Tag tag; ct.children) {
498 if (tag.name == "title") {
499 lay.pushStyles();
500 scope(exit) lay.popStyles;
501 putParas(tag);
502 lay.endPara(); // space
503 } else if (tag.name == "subtitle") {
504 lay.pushStyles();
505 scope(exit) lay.popStyles;
506 putParas(tag);
507 //lay.endPara(); // space
510 lay.endPara;
511 foreach (Tag tag; ct.children) {
512 if (tag.name.length == 0) continue;
513 if (tag.name == "text-author") continue;
514 if (tag.name == "title") continue;
515 if (tag.name == "subtitle") continue;
516 if (tag.name == "epigraph") badTag(tag, "epigraphs in poems are not supported yet");
517 if (tag.name == "date") continue;
518 if (tag.name == "stanza") { putStanza(tag); lay.put(LayText.EndParaCh); continue; }
519 badTag(tag);
521 putAuthor(ct);
523 putPoemx = &putPoem;
525 void putSection (Tag sc) {
526 bool sectionRegistered = false;
528 void registerSection (Tag tag) {
529 import std.conv : to;
530 if (sectionRegistered) return;
531 sectionRegistered = true;
532 string text = tag.textContent.xstrip;
533 //lay.sections ~= lay.curWordIndex;
534 string name;
535 while (text.length) {
536 char ch = text.ptr[0];
537 text = text[1..$];
538 if (ch <= ' ' || ch == 127) {
539 if (name.length == 0) continue;
540 if (ch != '\n') ch = ' ';
541 if (ch == '\n') {
542 if (name[$-1] > ' ') name ~= "\n...";
543 text = text.xstrip;
544 } else {
545 if (name[$-1] > ' ') name ~= ' ';
547 } else {
548 name ~= ch;
551 name = xstrip(name);
552 if (name.length == 0) name = "* * *";
553 meta.sections ~= BookMetadata.Anc(name.to!dstring, lay.nextWordIndex);
556 saveTagId(sc);
557 foreach (Tag tag; sc.children) {
558 if (tag.name.length == 0) continue;
559 if (tag.name == "title") {
560 lay.pushStyles();
561 scope(exit) lay.popStyles;
562 setTitleStyle(lay);
563 registerSection(tag);
564 putParas(tag);
565 } else if (tag.name == "subtitle") {
566 lay.pushStyles();
567 scope(exit) lay.popStyles;
568 setSubtitleStyle(lay);
569 registerSection(tag);
570 putParas(tag);
571 } else if (tag.name == "epigraph") {
572 putEpigraph(tag);
573 } else if (tag.name == "section") {
574 lay.pushStyles();
575 scope(exit) lay.popStyles;
576 setNormalStyle(lay);
577 putSection(tag);
578 } else if (tag.name == "p") {
579 putTagContents(tag);
580 } else if (tag.name == "image") {
581 lay.pushStyles();
582 scope(exit) lay.popStyles;
583 lay.lineStyle.leftpad = 0;
584 lay.lineStyle.rightpad = 0;
585 lay.lineStyle.paraIndent = 0;
586 lay.lineStyle.setCenter;
587 putImage(tag);
588 lay.endPara();
589 } else if (tag.name == "empty-line") {
590 lay.endPara();
591 } else if (tag.name == "cite") {
592 putCite(tag);
593 } else if (tag.name == "table") {
594 lay.pushStyles();
595 scope(exit) lay.popStyles;
596 lay.fontStyle.fontsize += 8;
597 lay.lineStyle.setCenter;
598 lay.put("TABLE SKIPPED");
599 lay.endPara();
600 } else if (tag.name == "poem") {
601 putPoem(tag);
602 } else if (tag.name == "image") {
603 } else if (tag.name == "style") {
604 } else if (tag.name == "site" || tag.name == "annotation") {
605 } else {
606 badTag(tag);
609 lay.endPara;
612 foreach (Tag tag; book.content.children) {
613 if (tag.name == "title") {
614 lay.pushStyles();
615 scope(exit) lay.popStyles;
616 setTitleStyle(lay);
617 lay.endPara();
618 putParas(tag);
619 lay.endPara();
620 } else if (tag.name == "subtitle") {
621 lay.pushStyles();
622 scope(exit) lay.popStyles;
623 setTitleStyle(lay);
624 putParas(tag);
625 lay.endPara();
626 } else if (tag.name == "epigraph") {
627 putEpigraph(tag);
628 } else if (tag.name == "section") {
629 lay.pushStyles();
630 scope(exit) lay.popStyles;
631 setNormalStyle(lay);
632 putSection(tag);
633 } else if (tag.name == "image") {
634 putImage(tag);
638 lay.finalize();
640 return meta;
644 // ////////////////////////////////////////////////////////////////////////// //
645 private __gshared LayFontStash laf; // layouter font stash
648 // ////////////////////////////////////////////////////////////////////////// //
649 private void loadFmtFonts () {
650 laf = new LayFontStash();
652 laf.addFont("text", textFontName);
653 laf.addFont("texti", textiFontName);
654 laf.addFont("textb", textbFontName);
655 laf.addFont("textz", textzFontName);
657 laf.addFont("mono", monoFontName);
658 laf.addFont("monoi", monoiFontName);
659 laf.addFont("monob", monobFontName);
660 laf.addFont("monoz", monozFontName);
664 // ////////////////////////////////////////////////////////////////////////// //
665 // messages
666 struct ReformatWork {
667 shared(BookText) booktext;
668 string bookFileName;
669 int w, h;
672 struct ReformatWorkComplete {
673 int w, h;
674 shared(BookText) booktext;
675 shared(LayText) laytext;
676 shared(BookMetadata) meta;
679 struct QuitWork {
683 // ////////////////////////////////////////////////////////////////////////// //
684 void reformatThreadFn (Tid ownerTid) {
685 bool doQuit = false;
686 loadFmtFonts();
687 BookText book;
688 string newFileName;
689 int newW = -1, newH = -1;
690 while (!doQuit) {
691 book = null;
692 newFileName = null;
693 newW = newH = -1;
694 receive(
695 (ReformatWork w) {
696 //{ conwriteln("reformat request received..."); }
697 book = cast(BookText)w.booktext;
698 newFileName = w.bookFileName;
699 newW = w.w;
700 newH = w.h;
701 if (newW < 1) newW = 1;
702 if (newH < 1) newH = 1;
704 (QuitWork w) {
705 doQuit = true;
708 if (!doQuit && newW > 0 && newH > 0) {
709 try {
710 if (book is null) {
711 //conwriteln("loading new book: '", newFileName, "'");
712 book = loadBook(newFileName);
715 int maxWidth = newW-4-2-BND_SCROLLBAR_WIDTH-2;
716 if (maxWidth < 64) maxWidth = 64;
718 // layout text
719 //conwriteln("layouting...");
720 auto stt = MonoTime.currTime;
721 auto lay = new LayText(laf, maxWidth);
722 lay.fontStyle.color = colorText.asUint;
724 auto meta = book.formatBook(lay);
726 auto ett = MonoTime.currTime-stt;
727 conwriteln("layouted in ", ett.total!"msecs", " milliseconds");
728 auto res = ReformatWorkComplete(newW, newH, cast(shared)book, cast(shared)lay, cast(shared)meta);
729 send(ownerTid, res);
730 } catch (Throwable e) {
731 // here, we are dead and fucked (the exact order doesn't matter)
732 import core.stdc.stdlib : abort;
733 import core.stdc.stdio : fprintf, stderr;
734 import core.memory : GC;
735 import core.thread : thread_suspendAll;
736 GC.disable(); // yeah
737 thread_suspendAll(); // stop right here, you criminal scum!
738 auto s = e.toString();
739 fprintf(stderr, "\n=== FATAL ===\n%.*s\n", cast(uint)s.length, s.ptr);
740 abort(); // die, you bitch!
744 send(ownerTid, QuitWork());