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 0000000..655a094 Binary files /dev/null and b/screenshot.png differ