From dc9092e312dc3664c74547e8cb86ca44acb920d6 Mon Sep 17 00:00:00 2001 From: Alec Murphy Date: Sun, 31 Jul 2022 15:39:08 -0400 Subject: [PATCH] Add files to repository --- KeyDev.HC | 48 ++++++++++++++ Load.HC | 11 ++++ Patch.HC | 88 +++++++++++++++++++++++++ README.md | 19 +++++- Run.HC | 3 + Saphir.HC | 19 ++++++ Status.HC | 7 ++ Win.HC | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ screenshot.png | Bin 0 -> 13736 bytes 9 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 KeyDev.HC create mode 100644 Load.HC create mode 100644 Patch.HC create mode 100644 Run.HC create mode 100644 Saphir.HC create mode 100644 Status.HC create mode 100644 Win.HC create mode 100644 screenshot.png diff --git a/KeyDev.HC b/KeyDev.HC new file mode 100644 index 0000000..e8fded3 --- /dev/null +++ b/KeyDev.HC @@ -0,0 +1,48 @@ +U0 @saphir_key_nop() {} + +Bool @saphir_put_key(I64 ch, I64 sc) { + if (sc & SCF_ALT && !(sc & SCF_CTRL)) { + switch (ch) { + case 0: + switch (sc.u8[0]) { + case SC_CURSOR_UP: + @saphir_win_select(SAPHIR_WIN_UP); + return TRUE; + case SC_CURSOR_DOWN: + @saphir_win_select(SAPHIR_WIN_DOWN); + return TRUE; + case SC_CURSOR_LEFT: + @saphir_win_select(SAPHIR_WIN_LEFT); + return TRUE; + case SC_CURSOR_RIGHT: + @saphir_win_select(SAPHIR_WIN_RIGHT); + return TRUE; + } + break; + case 'h': + @saphir_split_horz; + return TRUE; + break; + case 'v': + @saphir_split_vert; + return TRUE; + break; + } + } + return FALSE; +} + +U64 @tos_fp_cbs_enabled = + keydev.fp_ctrl_alt_cbs; // Save pointer to TempleOS system-wide (CTRL-ALT) + // callbacks + +U64 @tos_fp_cbs_disabled = CAlloc(0xD0); +keydev.fp_ctrl_alt_cbs = + @tos_fp_cbs_disabled; // Disable TempleOS system-wide (CTRL-ALT) callbacks + +// FIXME: Ideally, we would add a new KeyDev here, but since we need to override +// the HomeKeyPlugIns, we will need to patch MyKeyDev instead. + +// KeyDevAdd(&@saphir_put_key, &MyPutS, 0x10000000, +// TRUE); // Enable Saphir keyboard shortcuts +@function_patch(&MyPutKey, &@saphir_put_key); diff --git a/Load.HC b/Load.HC new file mode 100644 index 0000000..91c3cc6 --- /dev/null +++ b/Load.HC @@ -0,0 +1,11 @@ +/* clang-format off */ + +#include "Patch"; +#include "Win"; +#include "KeyDev"; +#include "Status"; +#include "Saphir"; + +/* clang-format on */ + +WinTileHorz; \ No newline at end of file diff --git a/Patch.HC b/Patch.HC new file mode 100644 index 0000000..6f6549a --- /dev/null +++ b/Patch.HC @@ -0,0 +1,88 @@ +U0 @function_patch(U32 from, U32 to) { + *(from(U8 *)) = 0xE9; + *((from + 1)(I32 *)) = to - from - 5; +} + +U0 @gr_update_text_bg2() { + I64 reg RSI *dst = gr.dc2->body, reg R13 c, row, col, num_rows = TEXT_ROWS, + num_cols = TEXT_COLS, i, j, cur_ch, + reg R12 w1 = gr.dc2->width_internal, w2 = -7 * w1 + 8, + w3 = 7 * w1, w4 = 0; + U32 *src = gr.text_base; + Bool blink_flag = Blink; + U8 *dst2 = dst; + + if (gr.pan_text_x || gr.hide_col) { + gr.pan_text_x = ClampI64(gr.pan_text_x, -7, 7); + j = AbsI64(gr.pan_text_x) / FONT_WIDTH + 1; + num_cols -= j; + if (gr.pan_text_x < 0) { + src += j; + i = FONT_WIDTH * j + gr.pan_text_x; + } else + i = gr.pan_text_x; + dst2 = dst(U8 *) + i; + w4 = j; + w3 += j * FONT_WIDTH; + + j *= FONT_WIDTH; + dst(U8 *) = gr.dc2->body; + for (row = num_rows * FONT_HEIGHT; row--;) { + for (col = i; col--;) + *dst(U8 *)++ = 0; + dst(U8 *) += w1 - i - j; + for (col = j; col--;) + *dst(U8 *)++ = 0; + } + } + dst = dst2; + + if (gr.pan_text_y || gr.hide_row) { + gr.pan_text_y = ClampI64(gr.pan_text_y, -7, 7); + j = AbsI64(gr.pan_text_y) / FONT_HEIGHT + 1; + num_rows -= j; + if (gr.pan_text_y < 0) { + src += w1 / FONT_WIDTH * j; + i = w1 * (FONT_HEIGHT * j + gr.pan_text_y); + } else + i = w1 * gr.pan_text_y; + dst2 = dst(U8 *) + i; + + j *= w1 * FONT_HEIGHT; + dst(U8 *) = gr.dc2->body; + for (row = i; row--;) + *dst(U8 *)++ = 0; + dst(U8 *) = + gr.dc2->body + TEXT_ROWS * TEXT_COLS * FONT_HEIGHT * FONT_WIDTH - j; + for (row = j; row--;) + *dst(U8 *)++ = 0; + } + dst = dst2; + + for (row = num_rows; row--;) { + for (col = num_cols; col--;) { + cur_ch = *src++; + if (cur_ch & (ATTRF_SEL | ATTRF_INVERT | ATTRF_BLINK)) { + if (cur_ch & ATTRF_SEL) + cur_ch.u8[1] = cur_ch.u8[1] ^ 0xFF; + if (cur_ch & ATTRF_INVERT) + cur_ch.u8[1] = cur_ch.u8[1] << 4 + cur_ch.u8[1] >> 4; + if (cur_ch & ATTRF_BLINK && blink_flag) + cur_ch.u8[1] = 0x30; + else + cur_ch.u8[1] = 0xFF; + } + c = gr.to_8_colors[cur_ch.u8[1] >> 4]; + MOV U64[RSI], R13 ADD RSI, R12 MOV U64[RSI], R13 ADD RSI, + R12 MOV U64[RSI], R13 ADD RSI, R12 MOV U64[RSI], R13 ADD RSI, + R12 MOV U64[RSI], R13 ADD RSI, R12 MOV U64[RSI], R13 ADD RSI, + R12 MOV U64[RSI], R13 ADD RSI, R12 MOV U64[RSI], R13 dst(U8 *) += w2; + } + src += w4; + dst(U8 *) += w3; + } +} + +@function_patch(&GrUpdateTextBG, + &@gr_update_text_bg2); // Patch GrUpdateTextBG to make cursor + // blinking less irritating \ No newline at end of file diff --git a/README.md b/README.md index 8ff9bb9..540a6a6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ # saphir -Tiling window extensions for TempleOS WinMgr \ No newline at end of file +Tiling window extensions for TempleOS WinMgr + +![saphir](https://git.checksum.fail/alec/saphir/raw/branch/master/screenshot.png? "saphir") + +# Info + +Saphir extends the existing TempleOS WinMgr to provide tiling window functionality, as well as provide some sane defaults to those who prefer a less "blinky" interface. Saphir does not require you to recompile your Kernel; any changes to existing functions are live-patched and not persistent. + +# Usage + +`#include "Run";` + +# Keyboard shortcuts + +`ALT` + arrow keys : navigate windows +`ALT` + `h` : split window horizontal +`ALT` + `v` : split window vertical + diff --git a/Run.HC b/Run.HC new file mode 100644 index 0000000..6d2bfee --- /dev/null +++ b/Run.HC @@ -0,0 +1,3 @@ +Adam("StrPrint(Fs->cur_dir, \"/\");\n"); +Adam("Fs->cur_dv = Let2Drv('T');\n"); +AdamFile("Load"); \ No newline at end of file diff --git a/Saphir.HC b/Saphir.HC new file mode 100644 index 0000000..4059d07 --- /dev/null +++ b/Saphir.HC @@ -0,0 +1,19 @@ +U0 SaphirTask() { + I64 count; + I64 i; + CTask *task; + while (1) { + count = @windowed_task_count; + for (i = 0; i < count; i++) { + task = @windowed_task_index(i); + @set_border_doc_for_win(task); + @set_cursor_for_focused_win(task); + @draw_saphir_border_for_win(task); + @ensure_win_no_overlap_status_bar(task); + } + @update_status_bar; + Sleep(1); + } +} + +Spawn(&SaphirTask, , "Saphir"); \ No newline at end of file diff --git a/Status.HC b/Status.HC new file mode 100644 index 0000000..dc4142b --- /dev/null +++ b/Status.HC @@ -0,0 +1,7 @@ +U0 @update_status_bar() { + gr.dc->color = BLACK; + GrRect(gr.dc, 0, GR_HEIGHT - 8, GR_WIDTH, 8); + gr.dc->color = LTGRAY; + GrPrint(gr.dc, 0, GR_HEIGHT - 8, "[0x%08x] %s", sys_focus_task, + sys_focus_task->task_title); +} \ No newline at end of file diff --git a/Win.HC b/Win.HC new file mode 100644 index 0000000..4227787 --- /dev/null +++ b/Win.HC @@ -0,0 +1,175 @@ +#define SAPHIR_WIN_UP 0 +#define SAPHIR_WIN_DOWN 1 +#define SAPHIR_WIN_LEFT 2 +#define SAPHIR_WIN_RIGHT 3 + +CDoc *SAPHIR_BORDER_DOC = DocNew; + +CTask *@is_task_windowed(CTask *task) { + if ((task->display_flags & 1 << DISPLAYf_SHOW) && + ((task->display_flags & 1 << DISPLAYf_NOT_RAW))) + return task; + return NULL; +} + +I64 @windowed_task_count() { + CTask *task; + I64 count = 0; + task = adam_task->next_task; + while (task != adam_task) { + if (@is_task_windowed(task)) + count++; + task = task->next_task; + } + return count; +} + +CTask *@windowed_task_index(I64 index) { + CTask *task; + I64 count = 0; + task = adam_task->next_task; + while (task != adam_task) { + if (@is_task_windowed(task)) { + if (count == index) + return task; + count++; + } + task = task->next_task; + } + return NULL; +} + +U0 @set_border_doc_for_win(CTask *task) { + task->border_doc = SAPHIR_BORDER_DOC; +} + +U0 @draw_saphir_border_for_win(CTask *task) { + I64 color = LTGRAY; + if (task == sys_focus_task) + color = LTRED; + I64 x; + I64 y; + I64 wl = task->win_left - 1; + I64 wr = task->win_right + 1; + I64 wt = task->win_top - 1; + I64 wb = task->win_bottom + 1; + for (x = wl; x < wr + 1; x++) { + gr.text_base[(wt * TEXT_COLS) + x].u8[1] = color; + gr.text_base[(wb * TEXT_COLS) + x].u8[1] = color; + } + for (y = wt; y < wb + 1; y++) { + gr.text_base[(y * TEXT_COLS) + wl].u8[1] = color; + gr.text_base[(y * TEXT_COLS) + wr].u8[1] = color; + } +} + +U0 @ensure_win_no_overlap_status_bar(CTask *task) { + task->win_bottom = MinI64(task->win_bottom, TEXT_ROWS - 3); +} + +U0 @set_cursor_for_focused_win(CTask *task) { + if (task == sys_focus_task) { + task->put_doc->flags &= ~(1 << DOCf_HIDE_CURSOR); + } else { + task->put_doc->flags |= (1 << DOCf_HIDE_CURSOR); + } +} + +U0 @saphir_win_select(I64 dir) { + CTask *task = sys_focus_task; + I64 wt = task->win_top; + I64 wl = task->win_left; + I64 i; + I64 j; + + switch (dir) { + case SAPHIR_WIN_UP: + i = wt - 1; + j = -1; + break; + case SAPHIR_WIN_DOWN: + i = wt + 1; + j = 1; + break; + case SAPHIR_WIN_LEFT: + i = wl - 1; + j = -1; + break; + case SAPHIR_WIN_RIGHT: + i = wl + 1; + j = 1; + break; + } + + CTask *task1; + + switch (dir) { + case SAPHIR_WIN_UP: + case SAPHIR_WIN_DOWN: + for (i = i; i > 0 && i < TEXT_ROWS + 1; i += j) { + task1 = adam_task->next_task; + while (task1 != adam_task) { + if (@is_task_windowed(task1)) { + if (task1->win_top == i && task1->win_left == wl) { + WinFocus(task1); + return; + } + } + task1 = task1->next_task; + } + } + break; + case SAPHIR_WIN_LEFT: + case SAPHIR_WIN_RIGHT: + for (i = i; i > 0 && i < TEXT_COLS + 1; i += j) { + task1 = adam_task->next_task; + while (task1 != adam_task) { + if (@is_task_windowed(task1)) { + if (task1->win_left == i) { + WinFocus(task1); + return; + } + } + task1 = task1->next_task; + } + } + break; + default: + break; + } +} + +U0 @saphir_split_horz() { + CTask *task1 = sys_focus_task; + I64 wt = task1->win_top; + I64 wl = task1->win_left; + I64 wb = task1->win_bottom; + I64 wr = task1->win_right; + + task1->win_bottom = wb / 2; + + CTask *task2 = User; + task2->win_top = (wb / 2) + 2; + task2->win_bottom = wb; + task2->win_left = wl; + task2->win_right = wr; + + WinZBufUpdate; +} + +U0 @saphir_split_vert() { + CTask *task1 = sys_focus_task; + I64 wt = task1->win_top; + I64 wb = task1->win_bottom; + I64 wr = task1->win_right; + + task1->win_right = wr / 2; + + CTask *task2 = User; + task2->win_top = wt; + task2->win_bottom = wb; + task2->win_left = (wr / 2) + 2; + task2->win_right = wr; + + WinZBufUpdate; +} \ No newline at end of file diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..655a0941bc27ff3621a824b99ffbd69ebe6cfa1d GIT binary patch literal 13736 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 literal 0 HcmV?d00001