Do not use backslash-b to select backspaces.
[mp-5.x.git] / mp_core.c
blob121322f82d883c6420596272a0a30dba7af6aa8e
1 /*
3 Minimum Profit - Programmer Text Editor
5 Copyright (C) 1991-2010 Angel Ortega <angel@triptico.com>
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 http://www.triptico.com
25 #include "config.h"
27 #include <stdio.h>
28 #include <wchar.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #include "mpdm.h"
33 #include "mpsl.h"
35 #include "mp.h"
37 /** data **/
39 /* exit requested? */
40 int mp_exit_requested = 0;
42 /* main namespace */
43 mpdm_t mp = NULL;
45 /** private data for drawing syntax-highlighted text **/
47 struct drw_1_info {
48 mpdm_t txt; /* the document */
49 mpdm_t syntax; /* syntax highlight information */
50 mpdm_t colors; /* color definitions (for attributes) */
51 mpdm_t word_color_func; /* word color function (just for detection) */
52 mpdm_t last_search; /* last search regex */
53 int normal_attr; /* normal attr */
54 int cursor_attr; /* cursor attr */
55 int n_lines; /* number of processed lines */
56 int p_lines; /* number of prereaded lines */
57 int t_lines; /* total lines in document */
58 int vx; /* first visible column */
59 int vy; /* first visible line */
60 int tx; /* horizontal window size */
61 int ty; /* vertical window size */
62 int tab_size; /* tabulator size */
63 int mod; /* modify count */
64 int preread_lines; /* lines to pre-read (for synhi blocks) */
65 int mark_eol; /* mark end of lines */
66 int redraw; /* redraw trigger */
69 struct drw_1_info drw_1;
70 struct drw_1_info drw_1_o;
72 static struct {
73 int x; /* cursor x */
74 int y; /* cursor y */
75 int *offsets; /* offsets of lines */
76 char *attrs; /* attributes */
77 int visible; /* offset to the first visible character */
78 int cursor; /* offset to cursor */
79 wchar_t *ptr; /* pointer to joined data */
80 int size; /* size of joined data */
81 int matchparen_offset; /* offset to matched paren */
82 int matchparen_o_attr; /* original attribute there */
83 int cursor_o_attr; /* original attribute under cursor */
84 mpdm_t v; /* the data */
85 mpdm_t old; /* the previously generated array */
86 int mark_offset; /* offset to the marked block */
87 int mark_size; /* size of mark_o_attr */
88 char *mark_o_attr; /* saved attributes for the mark */
89 } drw_2;
91 /** code **/
93 #define MP_REAL_TAB_SIZE(x) (drw_1.tab_size - ((x) % drw_1.tab_size))
95 static int drw_wcwidth(int x, wchar_t c)
96 /* returns the wcwidth of c, or the tab spaces for
97 the x column if it's a tab */
99 int r;
101 switch (c) {
102 case L'\n':
103 r = 1;
104 break;
106 case L'\t':
107 r = MP_REAL_TAB_SIZE(x);
108 break;
110 default:
111 r = mpdm_wcwidth(c);
112 break;
115 return r < 0 ? 1 : r;
119 int drw_vx2x(mpdm_t str, int vx)
120 /* returns the character in str that is on column vx */
122 const wchar_t *ptr = str->data;
123 int n, x;
125 for (n = x = 0; n < vx && ptr[x] != L'\0'; x++)
126 n += drw_wcwidth(n, ptr[x]);
128 return x;
132 int drw_x2vx(mpdm_t str, int x)
133 /* returns the column where the character at offset x seems to be */
135 const wchar_t *ptr = str->data;
136 int n, vx;
138 for (n = vx = 0; n < x && ptr[n] != L'\0'; n++)
139 vx += drw_wcwidth(vx, ptr[n]);
141 return vx;
145 static int drw_line_offset(int l)
146 /* returns the offset into v for line number l */
148 return drw_2.offsets[l - drw_1.vy + drw_1.p_lines];
152 static int drw_adjust_y(int y, int *vy, int ty)
153 /* adjusts the visual y position */
155 int t = *vy;
157 /* is y above the first visible line? */
158 if (y < *vy)
159 *vy = y;
161 /* is y below the last visible line? */
162 if (y > *vy + (ty - 2))
163 *vy = y - (ty - 2);
165 return t != *vy;
169 static int drw_adjust_x(int x, int y, int *vx, int tx, wchar_t * ptr)
170 /* adjust the visual x position */
172 int n, m;
173 int t = *vx;
175 /* calculate the column for the cursor position */
176 for (n = m = 0; n < x; n++, ptr++)
177 m += drw_wcwidth(m, *ptr);
179 /* if new cursor column is nearer the leftmost column, set */
180 if (m < *vx)
181 *vx = m;
183 /* if new cursor column is further the rightmost column, set */
184 if (m > *vx + (tx - 1))
185 *vx = m - (tx - 1);
187 return t != *vx;
191 static int drw_get_attr(wchar_t * color_name)
192 /* returns the attribute number for a color */
194 mpdm_t v;
195 int attr = 0;
197 if ((v = mpdm_hget_s(drw_1.colors, color_name)) != NULL)
198 attr = mpdm_ival(mpdm_hget_s(v, L"attr"));
200 return attr;
204 static int drw_prepare(mpdm_t doc)
205 /* prepares the document for screen drawing */
207 mpdm_t window = mpdm_hget_s(mp, L"window");
208 mpdm_t config = mpdm_hget_s(mp, L"config");
209 mpdm_t txt = mpdm_hget_s(doc, L"txt");
210 mpdm_t lines = mpdm_hget_s(txt, L"lines");
211 int x = mpdm_ival(mpdm_hget_s(txt, L"x"));
212 int y = mpdm_ival(mpdm_hget_s(txt, L"y"));
213 int n;
215 drw_1.vx = mpdm_ival(mpdm_hget_s(txt, L"vx"));
216 drw_1.vy = mpdm_ival(mpdm_hget_s(txt, L"vy"));
217 drw_1.tx = mpdm_ival(mpdm_hget_s(window, L"tx"));
218 drw_1.ty = mpdm_ival(mpdm_hget_s(window, L"ty"));
219 drw_1.tab_size = mpdm_ival(mpdm_hget_s(config, L"tab_size"));
220 drw_1.mod = mpdm_ival(mpdm_hget_s(txt, L"mod"));
221 drw_1.preread_lines = mpdm_ival(mpdm_hget_s(config, L"preread_lines"));
222 drw_1.mark_eol = mpdm_ival(mpdm_hget_s(config, L"mark_eol"));
223 drw_1.t_lines = mpdm_size(lines);
225 /* adjust the visual y coordinate */
226 if (drw_adjust_y(y, &drw_1.vy, drw_1.ty))
227 mpdm_hset_s(txt, L"vy", MPDM_I(drw_1.vy));
229 /* adjust the visual x coordinate */
230 if (drw_adjust_x
231 (x, y, &drw_1.vx, drw_1.tx, mpdm_string(mpdm_aget(lines, y))))
232 mpdm_hset_s(txt, L"vx", MPDM_I(drw_1.vx));
234 /* get the maximum prereadable lines */
235 drw_1.p_lines =
236 drw_1.vy > drw_1.preread_lines ? drw_1.preread_lines : drw_1.vy;
238 /* maximum lines */
239 drw_1.n_lines = drw_1.ty + drw_1.p_lines;
241 /* get the mp.colors structure and the most used attributes */
242 drw_1.colors = mpdm_hget_s(mp, L"colors");
243 drw_1.normal_attr = drw_get_attr(L"normal");
244 drw_1.cursor_attr = drw_get_attr(L"cursor");
246 /* store the syntax highlight structure */
247 drw_1.syntax = mpdm_hget_s(doc, L"syntax");
249 drw_1.word_color_func = mpdm_hget_s(mp, L"word_color_func");
251 mpdm_ref(txt);
252 mpdm_unref(drw_1.txt);
253 drw_1.txt = txt;
255 drw_2.x = x;
256 drw_2.y = y;
258 /* last search regex */
259 drw_1.last_search = mpdm_hget_s(mp, L"last_search");
261 /* redraw trigger */
262 drw_1.redraw = mpdm_ival(mpdm_hget_s(mp, L"redraw_counter"));
264 /* compare drw_1 with drw_1_o; if they are the same,
265 no more expensive calculations on drw_2 are needed */
266 if (memcmp(&drw_1, &drw_1_o, sizeof(drw_1)) == 0)
267 return 0;
269 /* different; store now */
270 memcpy(&drw_1_o, &drw_1, sizeof(drw_1_o));
272 /* alloc space for line offsets */
273 drw_2.offsets = realloc(drw_2.offsets, drw_1.n_lines * sizeof(int));
275 drw_2.ptr = NULL;
276 drw_2.size = 0;
278 /* add first line */
279 drw_2.ptr = mpdm_pokev(drw_2.ptr, &drw_2.size,
280 mpdm_aget(lines, drw_1.vy - drw_1.p_lines));
282 /* first line start at 0 */
283 drw_2.offsets[0] = 0;
285 /* add the following lines and store their offsets */
286 for (n = 1; n < drw_1.n_lines; n++) {
287 /* add the separator */
288 drw_2.ptr =
289 mpdm_poke(drw_2.ptr, &drw_2.size, L"\n", 1, sizeof(wchar_t));
291 /* this line starts here */
292 drw_2.offsets[n] = drw_2.size;
294 /* now add it */
295 drw_2.ptr = mpdm_pokev(drw_2.ptr, &drw_2.size,
296 mpdm_aget(lines,
297 n + drw_1.vy - drw_1.p_lines));
300 drw_2.ptr = mpdm_poke(drw_2.ptr, &drw_2.size, L"", 1, sizeof(wchar_t));
301 drw_2.size--;
303 /* now create a value */
304 mpdm_unref(drw_2.v);
305 drw_2.v = mpdm_ref(MPDM_ENS(drw_2.ptr, drw_2.size));
307 /* alloc and init space for the attributes */
308 drw_2.attrs = realloc(drw_2.attrs, drw_2.size + 1);
309 memset(drw_2.attrs, drw_1.normal_attr, drw_2.size + 1);
311 drw_2.visible = drw_line_offset(drw_1.vy);
313 return 1;
317 static int drw_fill_attr(int attr, int offset, int size)
318 /* fill an attribute */
320 if (attr != -1)
321 memset(drw_2.attrs + offset, attr, size);
323 return offset + size;
327 static int drw_fill_attr_regex(int attr)
328 /* fills with an attribute the last regex match */
330 return drw_fill_attr(attr, mpdm_regex_offset, mpdm_regex_size);
334 static void drw_words(void)
335 /* fills the attributes for separate words */
337 mpdm_t r, t;
338 int o = drw_2.visible;
339 mpdm_t word_color = NULL;
340 mpdm_t word_color_func = NULL;
342 /* take the hash of word colors, if any */
343 if ((word_color = mpdm_hget_s(mp, L"word_color")) == NULL)
344 return;
346 /* get the regex for words */
347 if ((r = mpdm_hget_s(mp, L"word_regex")) == NULL)
348 return;
350 /* get the word color function */
351 word_color_func = mpdm_hget_s(mp, L"word_color_func");
353 while ((t = mpdm_regex(drw_2.v, r, o)) != NULL) {
354 int attr = -1;
355 mpdm_t v;
357 mpdm_ref(t);
359 if ((v = mpdm_hget(word_color, t)) != NULL)
360 attr = mpdm_ival(v);
361 else if (word_color_func != NULL)
362 attr = mpdm_ival(mpdm_exec_1(word_color_func, t, NULL));
364 o = drw_fill_attr_regex(attr);
366 mpdm_unref(t);
371 static void drw_multiline_regex(mpdm_t a, int attr)
372 /* sets the attribute to all matching (possibly multiline) regexes */
374 int n;
376 for (n = 0; n < mpdm_size(a); n++) {
377 mpdm_t r = mpdm_aget(a, n);
378 int o = 0;
380 /* if the regex is an array, it's a pair of
381 'match from this' / 'match until this' */
382 if (r->flags & MPDM_MULTIPLE) {
383 mpdm_t rs = mpdm_aget(r, 0);
384 mpdm_t re = mpdm_aget(r, 1);
386 while (!mpdm_is_null(mpdm_regex(drw_2.v, rs, o))) {
387 int s;
389 /* fill the matched part */
390 o = drw_fill_attr_regex(attr);
392 /* try to match the end */
393 if (!mpdm_is_null(mpdm_regex(drw_2.v, re, o))) {
394 /* found; fill the attribute
395 to the end of the match */
396 s = mpdm_regex_size + (mpdm_regex_offset - o);
398 else {
399 /* not found; fill to the end
400 of the document */
401 s = drw_2.size - o;
404 /* fill to there */
405 o = drw_fill_attr(attr, o, s);
408 else {
409 /* it's a scalar */
411 if (*mpdm_string(r) == L'%') {
412 /* it's a sscanf() expression */
413 mpdm_t v;
415 while ((v = mpdm_ref(mpdm_sscanf(drw_2.v, r, o)))
416 && mpdm_size(v) == 2) {
417 int i = mpdm_ival(mpdm_aget(v, 0));
418 int s = mpdm_ival(mpdm_aget(v, 1)) - i;
420 o = drw_fill_attr(attr, i, s);
422 mpdm_unref(v);
425 else {
426 /* it's a regex */
427 /* while the regex matches, fill attributes */
428 while (!mpdm_is_null(mpdm_regex(drw_2.v, r, o)))
429 o = drw_fill_attr_regex(attr);
436 static void drw_blocks(void)
437 /* fill attributes for multiline blocks */
439 mpdm_t defs;
440 int n;
442 /* no definitions? return */
443 if (drw_1.syntax == NULL
444 || (defs = mpdm_hget_s(drw_1.syntax, L"defs")) == NULL)
445 return;
447 for (n = 0; n < mpdm_size(defs); n += 2) {
448 mpdm_t attr;
449 mpdm_t list;
451 /* get the attribute */
452 attr = mpdm_aget(defs, n);
453 attr = mpdm_hget(drw_1.colors, attr);
454 attr = mpdm_hget_s(attr, L"attr");
456 /* get the list for this word color */
457 list = mpdm_aget(defs, n + 1);
459 drw_multiline_regex(list, mpdm_ival(attr));
464 static void drw_selection(void)
465 /* draws the selected block, if any */
467 mpdm_t mark;
468 int bx, by, ex, ey, vertical;
469 int so, eo;
470 int mby, mey;
471 int line_offset, next_line_offset;
472 int y;
473 int len;
474 int attr;
476 /* no mark? return */
477 if ((mark = mpdm_hget_s(drw_1.txt, L"mark")) == NULL)
478 return;
480 bx = mpdm_ival(mpdm_hget_s(mark, L"bx"));
481 by = mpdm_ival(mpdm_hget_s(mark, L"by"));
482 ex = mpdm_ival(mpdm_hget_s(mark, L"ex"));
483 ey = mpdm_ival(mpdm_hget_s(mark, L"ey"));
484 vertical = mpdm_ival(mpdm_hget_s(mark, L"vertical"));
486 /* if block is not visible, return */
487 if (ey < drw_1.vy || by >= drw_1.vy + drw_1.ty)
488 return;
490 so = by < drw_1.vy ? drw_2.visible : drw_line_offset(by) + bx;
491 eo = ey >= drw_1.vy + drw_1.ty ? drw_2.size : drw_line_offset(ey) + ex;
493 /* alloc space and save the attributes being destroyed */
494 drw_2.mark_offset = so;
495 drw_2.mark_size = eo - so + 1;
496 drw_2.mark_o_attr = malloc(eo - so + 1);
497 memcpy(drw_2.mark_o_attr, &drw_2.attrs[so], eo - so + 1);
499 if (vertical == 0) {
500 /* normal selection */
501 drw_fill_attr(drw_get_attr(L"selection"), so, eo - so);
503 else {
504 /* vertical selection */
505 mby = by < drw_1.vy ? drw_1.vy : by;
506 mey = ey >= drw_1.vy + drw_1.ty ? drw_1.vy + drw_1.ty : ey;
507 line_offset = drw_line_offset(mby);
508 attr = drw_get_attr(L"selection");
509 for (y = mby; y <= mey; y++) {
510 next_line_offset = drw_line_offset(y + 1);
511 len = next_line_offset - line_offset - 1;
512 so = bx > len ? -1 : bx;
513 eo = ex > len ? len : ex;
515 if (so >= 0 && eo >= so)
516 drw_fill_attr(attr, line_offset + so, eo - so + 1);
518 line_offset = next_line_offset;
524 static void drw_search_hit(void)
525 /* colorize the search hit, if any */
527 if (drw_1.last_search != NULL) {
528 mpdm_t l = mpdm_ref(MPDM_A(0));
530 mpdm_aset(l, drw_1.last_search, 0);
531 drw_multiline_regex(l, drw_get_attr(L"search"));
532 mpdm_unref(l);
537 static void drw_cursor(void)
538 /* fill the attribute for the cursor */
540 /* calculate the cursor offset */
541 drw_2.cursor = drw_line_offset(drw_2.y) + drw_2.x;
543 drw_2.cursor_o_attr = drw_2.attrs[drw_2.cursor];
544 drw_fill_attr(drw_1.cursor_attr, drw_2.cursor, 1);
548 static void drw_matching_paren(void)
549 /* highlights the matching paren */
551 int o = drw_2.cursor;
552 int i = 0;
553 wchar_t c;
555 /* by default, no offset has been found */
556 drw_2.matchparen_offset = -1;
558 /* find the opposite and the increment (direction) */
559 switch (drw_2.ptr[o]) {
560 case L'(':
561 c = L')';
562 i = 1;
563 break;
564 case L'{':
565 c = L'}';
566 i = 1;
567 break;
568 case L'[':
569 c = L']';
570 i = 1;
571 break;
572 case L')':
573 c = L'(';
574 i = -1;
575 break;
576 case L'}':
577 c = L'{';
578 i = -1;
579 break;
580 case L']':
581 c = L'[';
582 i = -1;
583 break;
586 /* if a direction is set, do the searching */
587 if (i) {
588 wchar_t s = drw_2.ptr[o];
589 int m = 0;
590 int l = i == -1 ? drw_2.visible - 1 : drw_2.size;
592 while (o != l) {
593 if (drw_2.ptr[o] == s) {
594 /* found the same */
595 m++;
597 else if (drw_2.ptr[o] == c) {
598 /* found the opposite */
599 if (--m == 0) {
600 /* found! fill and exit */
601 drw_2.matchparen_offset = o;
602 drw_2.matchparen_o_attr = drw_2.attrs[o];
603 drw_fill_attr(drw_get_attr(L"matching"), o, 1);
604 break;
608 o += i;
614 static mpdm_t drw_push_pair(mpdm_t l, int i, int a, wchar_t * tmp)
615 /* pushes a pair of attribute / string into l */
617 /* create the array, if doesn't exist yet */
618 if (l == NULL)
619 l = MPDM_A(0);
621 mpdm_ref(l);
623 /* finish the string */
624 tmp[i] = L'\0';
626 /* special magic: if the attribute is the
627 one of the cursor and the string is more than
628 one character, create two strings; the
629 cursor is over a tab */
630 if (a == drw_1.cursor_attr && i > 1) {
631 mpdm_push(l, MPDM_I(a));
632 mpdm_push(l, MPDM_NS(tmp, 1));
634 /* the rest of the string has the original attr */
635 a = drw_2.cursor_o_attr;
637 /* one char less */
638 tmp[i - 1] = L'\0';
641 /* store the attribute and the string */
642 mpdm_push(l, MPDM_I(a));
643 mpdm_push(l, MPDM_S(tmp));
645 mpdm_unrefnd(l);
647 return l;
651 #define BUF_LINE 128
653 static mpdm_t drw_line(int line)
654 /* creates a list of attribute / string pairs for the current line */
656 mpdm_t l = NULL;
657 int m, i, t, n;
658 int o = drw_2.offsets[line + drw_1.p_lines];
659 int a = drw_2.attrs[o];
660 wchar_t tmp[BUF_LINE];
661 wchar_t c;
663 /* loop while not beyond the right margin */
664 for (m = i = 0; m < drw_1.vx + drw_1.tx; m += t, o++) {
665 /* take char and size */
666 c = drw_2.ptr[o];
667 t = drw_wcwidth(m, c);
669 /* further the left margin? */
670 if (m >= drw_1.vx) {
671 /* if the attribute is different or we're out of
672 temporary space, push and go on */
673 if (drw_2.attrs[o] != a || i >= BUF_LINE - t - 1) {
674 l = drw_push_pair(l, i, a, tmp);
675 i = 0;
678 /* size is 1, unless it's a tab */
679 n = c == L'\t' ? t : 1;
681 /* fill tabs with spaces */
682 if (c == L'\0' || c == L'\t')
683 c = drw_1.mark_eol ? L'\xb7' : L' ';
685 /* fill EOLs with special marks or spaces */
686 if (c == L'\n')
687 c = drw_1.mark_eol ? L'\xb6' : L' ';
689 /* if next char will not fit, use a space */
690 if (m + t > drw_1.vx + drw_1.tx)
691 c = L' ';
693 else {
694 /* left filler */
695 n = m + t - drw_1.vx;
696 c = L' ';
699 /* fill the string */
700 for (; n > 0; n--)
701 tmp[i++] = c;
703 a = drw_2.attrs[o];
705 /* end of line? */
706 if (drw_2.ptr[o] == L'\0' || drw_2.ptr[o] == L'\n')
707 break;
710 return drw_push_pair(l, i, a, tmp);
714 static mpdm_t drw_as_array(void)
715 /* returns an mpdm array of ty elements, which are also arrays of
716 attribute - string pairs */
718 mpdm_t a;
719 int n;
721 /* the array of lines */
722 a = MPDM_A(drw_1.ty);
723 mpdm_ref(a);
725 /* push each line */
726 for (n = 0; n < drw_1.ty; n++)
727 mpdm_aset(a, drw_line(n), n);
729 mpdm_unrefnd(a);
731 return a;
735 static mpdm_t drw_optimize_array(mpdm_t a, int optimize)
736 /* optimizes the array, NULLifying all lines that are the same as the last time */
738 mpdm_t o = drw_2.old;
739 mpdm_t r = a;
741 mpdm_ref(a);
743 if (optimize && o != NULL) {
744 int n = 0;
746 /* creates a copy */
747 r = mpdm_clone(a);
749 mpdm_ref(r);
751 /* compare each array */
752 while (n < mpdm_size(o) && n < mpdm_size(r)) {
753 /* if both lines are equal, optimize out */
754 if (mpdm_cmp(mpdm_aget(o, n), mpdm_aget(r, n)) == 0)
755 mpdm_aset(r, NULL, n);
757 n++;
760 mpdm_unrefnd(r);
763 mpdm_ref(a);
764 mpdm_unref(drw_2.old);
765 drw_2.old = a;
767 mpdm_unref(a);
769 return r;
773 static void drw_restore_attrs(void)
774 /* restored the patched attrs */
776 /* matching paren, if any */
777 if (drw_2.matchparen_offset != -1)
778 drw_fill_attr(drw_2.matchparen_o_attr, drw_2.matchparen_offset, 1);
780 /* cursor */
781 drw_fill_attr(drw_2.cursor_o_attr, drw_2.cursor, 1);
783 /* marked block, if any */
784 if (drw_2.mark_o_attr != NULL) {
785 memcpy(&drw_2.attrs[drw_2.mark_offset], drw_2.mark_o_attr,
786 drw_2.mark_size);
788 free(drw_2.mark_o_attr);
789 drw_2.mark_o_attr = NULL;
794 static mpdm_t drw_draw(mpdm_t doc, int optimize)
795 /* main document drawing function: takes a document and returns an array of
796 arrays of attribute / string pairs */
798 mpdm_t r = NULL;
800 if (drw_prepare(doc)) {
801 /* colorize separate words */
802 drw_words();
804 /* colorize multiline blocks */
805 drw_blocks();
808 /* now set the marked block (if any) */
809 drw_selection();
811 /* colorize the search hit */
812 drw_search_hit();
814 /* the cursor */
815 drw_cursor();
817 /* highlight the matching paren */
818 drw_matching_paren();
820 /* convert to an array of string / atribute pairs */
821 r = drw_as_array();
823 /* optimize */
824 r = drw_optimize_array(r, optimize);
826 /* restore the patched attrs */
827 drw_restore_attrs();
829 return r;
833 /** interface **/
835 mpdm_t mp_draw(mpdm_t doc, int optimize)
836 /* main generic drawing function: if the document has a 'paint' code,
837 calls it; otherwise, call drw_draw() */
839 mpdm_t r = NULL;
840 static int ppp = 0; /* previous private paint */
841 mpdm_t f;
842 int n;
844 /* if previous paint was private, disable optimizations */
845 if (ppp)
846 optimize = ppp = 0;
848 if (doc != NULL) {
849 if ((f = mpdm_hget_s(doc, L"paint")) != NULL) {
850 ppp = 1;
851 r = mpdm_exec_2(f, doc, MPDM_I(optimize), NULL);
853 else
854 r = drw_draw(doc, optimize);
857 /* if there is a global post_paint list of functions, execute it */
858 if ((f = mpdm_hget_s(mp, L"post_paint")) != NULL) {
859 for (n = 0; n < mpdm_size(f); n++)
860 r = mpdm_exec_2(mpdm_aget(f, n), doc, r, NULL);
863 /* if doc has a post_paint list of functions, execute it */
864 if ((f = mpdm_hget_s(doc, L"post_paint")) != NULL) {
865 for (n = 0; n < mpdm_size(f); n++)
866 r = mpdm_exec_2(mpdm_aget(f, n), doc, r, NULL);
869 return r;
873 #define THR_SPEED_STEP 10
874 #define THR_MAX_SPEED 7
876 int mp_keypress_throttle(int keydown)
877 /* processes key acceleration and throttle */
879 static int keydowns = 0;
880 static int seq = 0;
881 int redraw = 0;
883 if (keydown) {
884 int speed;
886 /* as keydowns accumulate, speed increases, which is the number
887 of cycles the redraw will be skipped (up to a maximum) */
888 if ((speed = 1 + (++keydowns / THR_SPEED_STEP)) > THR_MAX_SPEED)
889 speed = THR_MAX_SPEED;
891 if (++seq % speed == 0)
892 redraw = 1;
894 else {
895 if (keydowns > 1)
896 redraw = 1;
898 keydowns = 0;
901 return redraw;
905 mpdm_t mp_active(void)
906 /* interface to mp.active() */
908 return mpdm_exec(mpdm_hget_s(mp, L"active"), NULL, NULL);
912 void mp_process_action(mpdm_t action)
913 /* interface to mp.process_action() */
915 mpdm_void(mpdm_exec_1
916 (mpdm_hget_s(mp, L"process_action"), action, NULL));
920 void mp_process_event(mpdm_t keycode)
921 /* interface to mp.process_event() */
923 mpdm_void(mpdm_exec_1
924 (mpdm_hget_s(mp, L"process_event"), keycode, NULL));
928 void mp_set_y(mpdm_t doc, int y)
929 /* interface to mp.set_y() */
931 mpdm_void(mpdm_exec_2
932 (mpdm_hget_s(mp, L"set_y"), doc, MPDM_I(y), NULL));
936 mpdm_t mp_build_status_line(void)
937 /* interface to mp.build_status_line() */
939 return mpdm_exec(mpdm_hget_s(mp, L"build_status_line"), NULL, NULL);
943 mpdm_t mp_get_history(mpdm_t key)
944 /* interface to mp.get_history() */
946 return mpdm_exec_1(mpdm_hget_s(mp, L"get_history"), key, NULL);
950 mpdm_t mp_get_doc_names(void)
951 /* interface to mp.get_doc_names() */
953 return mpdm_exec(mpdm_hget_s(mp, L"get_doc_names"), NULL, NULL);
957 mpdm_t mp_menu_label(mpdm_t action)
958 /* interface to mp.menu_label() */
960 return mpdm_exec_1(mpdm_hget_s(mp, L"menu_label"), action, NULL);
964 mpdm_t mp_pending_key(void)
965 /* interface to mp.pending_key() */
967 return mpdm_exec_1(mpdm_hget_s(mp, L"pending_key"), NULL, NULL);
971 mpdm_t mp_process_keyseq(mpdm_t key)
972 /* interface to mp.process_keyseq() */
974 return mpdm_exec_1(mpdm_hget_s(mp, L"process_keyseq"), key, NULL);
978 mpdm_t mp_exit(mpdm_t args, mpdm_t ctxt)
979 /* exit the editor (set mp_exit_requested) */
981 mp_exit_requested = 1;
983 return NULL;
987 static mpdm_t exit_requested(mpdm_t args, mpdm_t ctxt)
988 /* returns the value of the mp_exit_requested variable */
990 return MPDM_I(mp_exit_requested);
994 mpdm_t mp_vx2x(mpdm_t args, mpdm_t ctxt)
995 /* interface to drw_vx2x() */
997 return
998 MPDM_I(drw_vx2x
999 (mpdm_aget(args, 0), mpdm_ival(mpdm_aget(args, 1))));
1003 mpdm_t mp_x2vx(mpdm_t args, mpdm_t ctxt)
1004 /* interface to drw_x2vx() */
1006 return
1007 MPDM_I(drw_x2vx
1008 (mpdm_aget(args, 0), mpdm_ival(mpdm_aget(args, 1))));
1011 mpdm_t mp_plain_load(mpdm_t args, mpdm_t ctxt)
1012 /* loads a plain file into an array (highly optimized one) */
1014 mpdm_t f = mpdm_aget(args, 0);
1015 mpdm_t a;
1016 mpdm_t v;
1017 int chomped = 1;
1018 int eol = 1;
1020 a = MPDM_A(0);
1021 mpdm_ref(a);
1023 /* clean last seen EOL */
1024 mpdm_hset_s(mp, L"last_seen_eol", NULL);
1026 /* NOTE: this code rewrites a value, which is *illegal*,
1027 to avoid generating too much residual values */
1028 while ((v = mpdm_read(f)) != NULL) {
1029 wchar_t *ptr = (wchar_t *) v->data;
1030 int size = v->size;
1032 /* chomp */
1033 if (size && ptr[size - 1] == L'\n') {
1034 if (--size && ptr[size - 1] == L'\r') {
1035 --size;
1036 eol = 2;
1039 ptr[size] = L'\0';
1040 v->size = size;
1042 else
1043 chomped = 0;
1045 mpdm_push(a, v);
1048 /* if last line was chomped, add a last, empty one */
1049 if (chomped)
1050 mpdm_push(a, MPDM_LS(L""));
1052 /* store the last seen EOL */
1053 mpdm_hset_s(mp, L"last_seen_eol", MPDM_LS(eol == 2 ? L"\r\n" : L"\n"));
1055 mpdm_unrefnd(a);
1057 return a;
1061 void mp_startup(int argc, char *argv[])
1063 mpdm_t INC;
1065 mpsl_startup();
1067 /* reset the structures */
1068 memset(&drw_1, '\0', sizeof(drw_1));
1069 memset(&drw_1_o, '\0', sizeof(drw_1_o));
1071 /* create main namespace */
1072 mp = MPDM_H(0);
1073 mpdm_hset_s(mpdm_root(), L"mp", mp);
1075 /* basic functions and data */
1076 mpdm_hset_s(mp, L"x2vx", MPDM_X(mp_x2vx));
1077 mpdm_hset_s(mp, L"vx2x", MPDM_X(mp_vx2x));
1078 mpdm_hset_s(mp, L"exit", MPDM_X(mp_exit));
1079 mpdm_hset_s(mp, L"exit_requested", MPDM_X(exit_requested));
1080 mpdm_hset_s(mp, L"plain_load", MPDM_X(mp_plain_load));
1081 mpdm_hset_s(mp, L"window", MPDM_H(0));
1082 mpdm_hset_s(mp, L"drv", MPDM_H(0));
1084 /* version */
1085 mpdm_hset_s(mp, L"VERSION", MPDM_S(L"" VERSION));
1087 /* creates the INC (executable path) array */
1088 INC = mpdm_hset_s(mpdm_root(), L"INC", MPDM_A(0));
1090 /* add installed library path */
1091 mpdm_push(INC, mpdm_strcat(mpdm_hget_s(mpdm_root(), L"APPDIR"),
1092 MPDM_MBS(CONFOPT_APPNAME))
1095 if (!TRY_DRIVERS()) {
1096 printf("No usable driver found; exiting.\n");
1097 exit(1);
1100 mpsl_argv(argc, argv);
1104 void mp_mpsl(void)
1106 mpdm_t e;
1108 mpsl_eval(MPDM_LS(L"load('mp_core.mpsl');"), NULL, NULL);
1110 if ((e = mpdm_hget_s(mpdm_root(), L"ERROR")) != NULL) {
1111 mpdm_write_wcs(stdout, mpdm_string(e));
1112 printf("\n");
1117 void mp_shutdown(void)
1119 /* unref pending values */
1120 mpdm_unref(drw_1.txt);
1121 mpdm_unref(drw_2.v);
1122 mpdm_unref(drw_2.old);
1124 #ifdef DEBUG_CLEANUP
1125 mpdm_unref(mpdm_root());
1126 #endif
1128 mpsl_shutdown();
1132 int main(int argc, char *argv[])
1134 mp_startup(argc, argv);
1136 mp_mpsl();
1138 mp_shutdown();
1140 return 0;