/* file-changed.c - Handling file changed notifications. Copyright (C) 2002, 2003 Free Software Foundation, Inc. Written by Marcus Brinkmann. This file is part of the GNU Hurd. The GNU Hurd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The GNU Hurd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #include #include #include #include "cons.h" #include "fs_notify_S.h" kern_return_t cons_S_file_changed (cons_notify_t notify, natural_t tickno, file_changed_type_t change, off_t start, off_t end) { error_t err = 0; vcons_t vcons = (vcons_t) notify; if (!notify || notify->cons) return EOPNOTSUPP; mutex_lock (&vcons->lock); switch (change) { case FILE_CHANGED_NULL: /* Always sent first for sync. */ cons_vcons_refresh (vcons); break; case FILE_CHANGED_WRITE: /* File data has been written. */ while (vcons->state.changes.written < vcons->display->changes.written) { cons_change_t change; if (vcons->display->changes.written - vcons->state.changes.written > vcons->cons->slack) { cons_vcons_refresh (vcons); continue; } change = vcons->state.changes.buffer[vcons->state.changes.written % vcons->state.changes.length]; if (vcons->display->changes.written - vcons->state.changes.written > vcons->state.changes.length - 1) { /* While we were reading the entry, the server might have overwritten it. */ cons_vcons_refresh (vcons); continue; } vcons->state.changes.written++; if (change.what.not_matrix) { if (change.what.cursor_pos) { uint32_t old_row = vcons->state.cursor.row; uint32_t height = vcons->state.screen.height; uint32_t row; vcons->state.cursor.col = vcons->display->cursor.col; row = vcons->state.cursor.row = vcons->display->cursor.row; if (row + vcons->scrolling < height) { cons_vcons_set_cursor_pos (vcons, vcons->state.cursor.col, row + vcons->scrolling); if (old_row + vcons->scrolling >= height) /* The cursor was invisible before. */ cons_vcons_set_cursor_status (vcons, vcons->state.cursor.status); } else if (old_row + vcons->scrolling < height) /* The cursor was visible before. */ cons_vcons_set_cursor_status (vcons, CONS_CURSOR_INVISIBLE); cons_vcons_update (vcons); } if (change.what.cursor_status) { vcons->state.cursor.status = vcons->display->cursor.status; cons_vcons_set_cursor_status (vcons, vcons->state.cursor.status); cons_vcons_update (vcons); } if (change.what.screen_cur_line) { uint32_t new_cur_line; new_cur_line = vcons->display->screen.cur_line; if (new_cur_line != vcons->state.screen.cur_line) { off_t size = vcons->state.screen.width * vcons->state.screen.lines; off_t vis_start; uint32_t scrolling; off_t start; off_t end; if (new_cur_line > vcons->state.screen.cur_line) scrolling = new_cur_line - vcons->state.screen.cur_line; else scrolling = UINT32_MAX - vcons->state.screen.cur_line + 1 + new_cur_line; /* If we are scrolling back, defer scrolling until absolutely necessary. */ if (vcons->scrolling) { if (_cons_jump_down_on_output) _cons_vcons_scrollback (vcons, CONS_SCROLL_ABSOLUTE_LINE, 0); else { if (vcons->scrolling + scrolling <= vcons->state.screen.scr_lines) { vcons->scrolling += scrolling; scrolling = 0; } else { scrolling -= vcons->state.screen.scr_lines - vcons->scrolling; vcons->scrolling = vcons->state.screen.scr_lines; } } } if (scrolling) { uint32_t cur_disp_line; if (new_cur_line >= vcons->scrolling) cur_disp_line = new_cur_line - vcons->scrolling; else cur_disp_line = (UINT32_MAX - (vcons->scrolling - new_cur_line)) + 1; if (scrolling > vcons->state.screen.height) scrolling = vcons->state.screen.height; if (scrolling < vcons->state.screen.height) cons_vcons_scroll (vcons, scrolling); else cons_vcons_clear (vcons, vcons->state.screen.width * vcons->state.screen.height, 0, 0); vis_start = vcons->state.screen.width * (cur_disp_line % vcons->state.screen.lines); start = (((cur_disp_line % vcons->state.screen.lines) + vcons->state.screen.height - scrolling) * vcons->state.screen.width) % size; end = start + scrolling * vcons->state.screen.width - 1; cons_vcons_write (vcons, vcons->state.screen.matrix + start, end < size ? end - start + 1 : size - start, 0, vcons->state.screen.height - scrolling); if (end >= size) cons_vcons_write (vcons, vcons->state.screen.matrix, end - size + 1, 0, (size - vis_start) / vcons->state.screen.width); cons_vcons_update (vcons); } vcons->state.screen.cur_line = new_cur_line; } } if (change.what.screen_scr_lines) { vcons->state.screen.scr_lines = vcons->display->screen.scr_lines; if (vcons->state.screen.scr_lines < vcons->scrolling) assert (!"Implement shrinking scrollback buffer! XXX"); } if (change.what.bell_audible) { while (vcons->state.bell.audible < vcons->display->bell.audible) { if (_cons_audible_bell == BELL_AUDIBLE) cons_vcons_beep (vcons); else if (_cons_audible_bell == BELL_VISUAL) cons_vcons_flash (vcons); vcons->state.bell.audible++; } } if (change.what.bell_visible) { while (vcons->state.bell.visible < vcons->display->bell.visible) { if (_cons_visual_bell == BELL_VISUAL) cons_vcons_flash (vcons); else if (_cons_visual_bell == BELL_AUDIBLE) cons_vcons_beep (vcons); vcons->state.bell.visible++; } } if (change.what.flags) { uint32_t flags = vcons->display->flags; if ((flags & CONS_FLAGS_SCROLL_LOCK) != (vcons->state.flags & CONS_FLAGS_SCROLL_LOCK)) cons_vcons_set_scroll_lock (vcons, flags & CONS_FLAGS_SCROLL_LOCK); vcons->state.flags = flags; } } else { /* For clipping. */ off_t size = vcons->state.screen.width*vcons->state.screen.lines; off_t rotate; off_t vis_end = vcons->state.screen.height * vcons->state.screen.width - 1; off_t end2 = -1; off_t start_rel = 0; /* start relative to visible start. */ off_t start = change.matrix.start; off_t end = change.matrix.end; if (vcons->scrolling && _cons_jump_down_on_output) _cons_vcons_scrollback (vcons, CONS_SCROLL_ABSOLUTE_LINE, 0); if (vcons->state.screen.cur_line >= vcons->scrolling) rotate = vcons->state.screen.cur_line - vcons->scrolling; else rotate = (UINT32_MAX - (vcons->scrolling - vcons->state.screen.cur_line)) + 1; rotate = vcons->state.screen.width * (rotate % vcons->state.screen.lines); /* Rotate the buffer. */ start -= rotate; if (start < 0) start += size; end -= rotate; if (end < 0) end += size; /* Find the intersection. */ if (start > vis_end) { if (end < start) { start = 0; if (vis_end < end) end = vis_end; } else start = -1; } else { if (end >= start) { if (end > vis_end) end = vis_end; } else { end2 = end; end = vis_end; } } /* We now have three cases: No intersection if start == -1, one intersection [start;end] if end2 == -1, and two intersections [start;end] and [0;end2] if end2 != -1. However, we still have to undo the buffer rotation. */ if (start != -1) { start_rel = start; start += rotate; if (start >= size) start -= size; end += rotate; if (end >= size) end -= size; if (start > end) end += size; } if (end2 != -1) /* The interval should be [vis_start:end2]. */ end2 += rotate; if (start != -1) { cons_vcons_clear (vcons, end - start + 1, start_rel % vcons->state.screen.width, start_rel / vcons->state.screen.width); cons_vcons_write (vcons, vcons->state.screen.matrix + start, end < size ? end - start + 1 : size - start, start_rel % vcons->state.screen.width, start_rel / vcons->state.screen.width); if (end >= size) cons_vcons_write (vcons, vcons->state.screen.matrix, end - size + 1, (size - rotate) % vcons->state.screen.width, (size - rotate) / vcons->state.screen.width); if (end2 != -1) { cons_vcons_clear (vcons, end2 - rotate + 1, 0, 0); cons_vcons_write (vcons, vcons->state.screen.matrix + rotate, end2 < size ? end2 - rotate + 1 : size - rotate, 0, 0); if (end2 >= size) cons_vcons_write (vcons, vcons->state.screen.matrix, end2 - size + 1, (size - rotate) % vcons->state.screen.width, (size - rotate) / vcons->state.screen.width); } cons_vcons_update (vcons); } } } break; case FILE_CHANGED_EXTEND: /* File has grown. */ case FILE_CHANGED_TRUNCATE: /* File has been truncated. */ case FILE_CHANGED_META: /* Stat information has changed, and none of the previous three apply. Not sent for changes in node times. */ default: err = EINVAL; }; mutex_unlock (&vcons->lock); return err; }