mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05:00
Make it possible to invalidate only a portion of a window.
Use this in Terminal to only invalidate rows where anything changed.
This commit is contained in:
parent
9d7da26b4e
commit
dff70021ab
15 changed files with 91 additions and 15 deletions
|
@ -195,7 +195,7 @@ public:
|
|||
int gui$create_window(const GUI_CreateWindowParameters*);
|
||||
int gui$destroy_window(int window_id);
|
||||
int gui$get_window_backing_store(int window_id, GUI_WindowBackingStoreInfo*);
|
||||
int gui$invalidate_window(int window_id);
|
||||
int gui$invalidate_window(int window_id, const GUI_Rect*);
|
||||
|
||||
DisplayInfo get_display_info();
|
||||
|
||||
|
|
|
@ -99,16 +99,21 @@ int Process::gui$get_window_backing_store(int window_id, GUI_WindowBackingStoreI
|
|||
return 0;
|
||||
}
|
||||
|
||||
int Process::gui$invalidate_window(int window_id)
|
||||
int Process::gui$invalidate_window(int window_id, const GUI_Rect* rect)
|
||||
{
|
||||
dbgprintf("%s<%u> gui$invalidate_window (window_id=%d)\n", name().characters(), pid(), window_id);
|
||||
if (window_id < 0)
|
||||
return -EINVAL;
|
||||
if (rect && !validate_read_typed(rect))
|
||||
return -EFAULT;
|
||||
auto it = m_windows.find(window_id);
|
||||
if (it == m_windows.end())
|
||||
return -EBADWINDOW;
|
||||
auto& window = *(*it).value;
|
||||
WSEventLoop::the().post_event(&window, make<WSEvent>(WSEvent::WM_Invalidate));
|
||||
auto event = make<WSEvent>(WSEvent::WM_Invalidate);
|
||||
if (rect)
|
||||
event->set_rect(*rect);
|
||||
WSEventLoop::the().post_event(&window, move(event));
|
||||
WSEventLoop::the().server_process().request_wakeup();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
|
|||
case Syscall::SC_gui_get_window_backing_store:
|
||||
return current->gui$get_window_backing_store((int)arg1, (GUI_WindowBackingStoreInfo*)arg2);
|
||||
case Syscall::SC_gui_invalidate_window:
|
||||
return current->gui$invalidate_window((int)arg1);
|
||||
return current->gui$invalidate_window((int)arg1, (const GUI_Rect*)arg2);
|
||||
default:
|
||||
kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3);
|
||||
break;
|
||||
|
|
|
@ -9,9 +9,9 @@ int gui_create_window(const GUI_CreateWindowParameters* params)
|
|||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||
}
|
||||
|
||||
int gui_invalidate_window(int window_id)
|
||||
int gui_invalidate_window(int window_id, const GUI_Rect* rect)
|
||||
{
|
||||
int rc = syscall(SC_gui_invalidate_window, window_id);
|
||||
int rc = syscall(SC_gui_invalidate_window, window_id, rect);
|
||||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
__BEGIN_DECLS
|
||||
|
||||
int gui_create_window(const GUI_CreateWindowParameters* params);
|
||||
int gui_invalidate_window(int window_id);
|
||||
int gui_invalidate_window(int window_id, const GUI_Rect*);
|
||||
int gui_get_window_backing_store(int window_id, GUI_WindowBackingStoreInfo* info);
|
||||
|
||||
__END_DECLS
|
||||
|
|
|
@ -49,6 +49,7 @@ Terminal::Terminal()
|
|||
// Rightmost column is always last tab on line.
|
||||
m_horizontal_tabs[columns() - 1] = 1;
|
||||
|
||||
m_row_needs_invalidation = (bool*)(malloc(rows() * sizeof(bool)));
|
||||
m_buffer = (byte*)malloc(rows() * columns());
|
||||
m_attributes = (Attribute*)malloc(rows() * columns() * sizeof(Attribute));
|
||||
memset(m_buffer, ' ', m_rows * m_columns);
|
||||
|
@ -58,6 +59,7 @@ Terminal::Terminal()
|
|||
|
||||
Terminal::~Terminal()
|
||||
{
|
||||
free(m_row_needs_invalidation);
|
||||
free(m_buffer);
|
||||
free(m_attributes);
|
||||
free(m_horizontal_tabs);
|
||||
|
@ -408,6 +410,12 @@ Rect Terminal::glyph_rect(word row, word column)
|
|||
return { x + m_inset, y + m_inset, font().glyph_width(), font().glyph_height() };
|
||||
}
|
||||
|
||||
Rect Terminal::row_rect(word row)
|
||||
{
|
||||
int y = row * m_line_height;
|
||||
return { m_inset, y + m_inset, font().glyph_width() * m_columns, font().glyph_height() };
|
||||
}
|
||||
|
||||
inline Terminal::Attribute& Terminal::attribute_at(word row, word column)
|
||||
{
|
||||
return m_attributes[(row * m_columns) + column];
|
||||
|
@ -417,6 +425,10 @@ void Terminal::paint()
|
|||
{
|
||||
Rect rect { 0, 0, m_pixel_width, m_pixel_height };
|
||||
Painter painter(*m_backing);
|
||||
|
||||
bool need_full_invalidation = false;
|
||||
memset(m_row_needs_invalidation, 0, rows() * sizeof(bool));
|
||||
|
||||
#ifdef FAST_SCROLL
|
||||
if (m_rows_to_scroll_backing_store && m_rows_to_scroll_backing_store < m_rows) {
|
||||
int first_scanline = m_inset;
|
||||
|
@ -428,6 +440,7 @@ void Terminal::paint()
|
|||
m_backing->scanline(second_scanline),
|
||||
scanlines_to_copy * m_pixel_width
|
||||
);
|
||||
need_full_invalidation = true;
|
||||
attribute_at(m_cursor_row - m_rows_to_scroll_backing_store, m_cursor_column).dirty = true;
|
||||
}
|
||||
m_rows_to_scroll_backing_store = 0;
|
||||
|
@ -439,6 +452,7 @@ void Terminal::paint()
|
|||
if (!attribute.dirty)
|
||||
continue;
|
||||
attribute.dirty = false;
|
||||
m_row_needs_invalidation[row] = true;
|
||||
char ch = m_buffer[(row * m_columns) + (column)];
|
||||
auto character_rect = glyph_rect(row, column);
|
||||
auto character_background = ansi_color(attribute.background_color);
|
||||
|
@ -455,10 +469,30 @@ void Terminal::paint()
|
|||
else
|
||||
painter.draw_rect(cursor_rect, Color::MidGray);
|
||||
|
||||
if (m_belling)
|
||||
painter.draw_rect(rect, Color::Red);
|
||||
m_row_needs_invalidation[m_cursor_row] = true;
|
||||
|
||||
int rc = gui_invalidate_window(m_window_id);
|
||||
if (m_belling) {
|
||||
need_full_invalidation = true;
|
||||
painter.draw_rect(rect, Color::Red);
|
||||
}
|
||||
|
||||
if (need_full_invalidation) {
|
||||
invalidate_window();
|
||||
return;
|
||||
}
|
||||
|
||||
Rect invalidation_rect;
|
||||
for (int i = 0; i < m_rows; ++i) {
|
||||
if (m_row_needs_invalidation[i])
|
||||
invalidation_rect = invalidation_rect.united(row_rect(i));
|
||||
}
|
||||
invalidate_window(invalidation_rect);
|
||||
}
|
||||
|
||||
void Terminal::invalidate_window(const Rect& a_rect)
|
||||
{
|
||||
GUI_Rect rect = a_rect;
|
||||
int rc = gui_invalidate_window(m_window_id, a_rect.is_null() ? nullptr : &rect);
|
||||
if (rc < 0) {
|
||||
perror("gui_invalidate_window");
|
||||
exit(1);
|
||||
|
|
|
@ -25,6 +25,7 @@ private:
|
|||
void set_cursor(unsigned row, unsigned column);
|
||||
void put_character_at(unsigned row, unsigned column, byte ch);
|
||||
void invalidate_cursor();
|
||||
void invalidate_window(const Rect& = Rect());
|
||||
|
||||
void escape$A(const Vector<unsigned>&);
|
||||
void escape$D(const Vector<unsigned>&);
|
||||
|
@ -40,6 +41,7 @@ private:
|
|||
word columns() const { return m_columns; }
|
||||
word rows() const { return m_rows; }
|
||||
Rect glyph_rect(word row, word column);
|
||||
Rect row_rect(word row);
|
||||
|
||||
struct Attribute {
|
||||
Attribute() { reset(); }
|
||||
|
@ -58,6 +60,7 @@ private:
|
|||
|
||||
byte* m_buffer { nullptr };
|
||||
Attribute* m_attributes { nullptr };
|
||||
bool* m_row_needs_invalidation { nullptr };
|
||||
|
||||
word m_columns { 0 };
|
||||
word m_rows { 0 };
|
||||
|
|
|
@ -43,7 +43,7 @@ int main(int argc, char** argv)
|
|||
|
||||
paint(*bitmap, backing.size.width, backing.size.height);
|
||||
|
||||
rc = gui_invalidate_window(window_id);
|
||||
rc = gui_invalidate_window(window_id, nullptr);
|
||||
if (rc < 0) {
|
||||
perror("gui_invalidate_window");
|
||||
return 1;
|
||||
|
@ -69,7 +69,7 @@ int main(int argc, char** argv)
|
|||
|
||||
if (event.type == GUI_Event::Type::MouseDown) {
|
||||
paint(*bitmap, backing.size.width, backing.size.height);
|
||||
gui_invalidate_window(window_id);
|
||||
gui_invalidate_window(window_id, nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@ void Rect::intersect(const Rect& other)
|
|||
|
||||
Rect Rect::united(const Rect& other) const
|
||||
{
|
||||
if (is_null())
|
||||
return other;
|
||||
if (other.is_null())
|
||||
return *this;
|
||||
Rect rect;
|
||||
rect.set_left(min(left(), other.left()));
|
||||
rect.set_top(min(top(), other.top()));
|
||||
|
|
|
@ -20,9 +20,14 @@ public:
|
|||
}
|
||||
Rect(const GUI_Rect&);
|
||||
|
||||
bool is_null() const
|
||||
{
|
||||
return width() == 0 && height() == 0;
|
||||
}
|
||||
|
||||
bool is_empty() const
|
||||
{
|
||||
return width() == 0 || height() == 0;
|
||||
return width() <= 0 || height() <= 0;
|
||||
}
|
||||
|
||||
void move_by(int dx, int dy)
|
||||
|
|
|
@ -53,8 +53,15 @@ public:
|
|||
bool isKeyEvent() const { return m_type == KeyUp || m_type == KeyDown; }
|
||||
bool isPaintEvent() const { return m_type == Paint; }
|
||||
|
||||
Rect rect() const { return m_rect; }
|
||||
void set_rect(const GUI_Rect& rect)
|
||||
{
|
||||
m_rect = rect;
|
||||
}
|
||||
|
||||
private:
|
||||
Type m_type { Invalid };
|
||||
Rect m_rect;
|
||||
};
|
||||
|
||||
class PaintEvent final : public WSEvent {
|
||||
|
|
|
@ -83,7 +83,9 @@ void WSEventLoop::post_event(WSEventReceiver* receiver, OwnPtr<WSEvent>&& event)
|
|||
|
||||
if (event->type() == WSEvent::WM_Invalidate) {
|
||||
for (auto& queued_event : m_queued_events) {
|
||||
if (receiver == queued_event.receiver && queued_event.event->type() == WSEvent::WM_Invalidate) {
|
||||
if (receiver == queued_event.receiver
|
||||
&& queued_event.event->type() == WSEvent::WM_Invalidate
|
||||
&& (queued_event.event->rect().is_empty() || queued_event.event->rect().contains(event->rect()))) {
|
||||
#ifdef WSEVENTLOOP_DEBUG
|
||||
dbgprintf("Swallow WM_Invalidate\n");
|
||||
#endif
|
||||
|
|
|
@ -75,7 +75,7 @@ void WSWindow::event(WSEvent& event)
|
|||
gui_event.key.character = static_cast<KeyEvent&>(event).text()[0];
|
||||
break;
|
||||
case WSEvent::WM_Invalidate:
|
||||
WSWindowManager::the().invalidate(*this);
|
||||
WSWindowManager::the().invalidate(*this, event.rect());
|
||||
return;
|
||||
case WSEvent::WindowActivated:
|
||||
gui_event.type = GUI_Event::Type::WindowActivated;
|
||||
|
|
|
@ -412,6 +412,21 @@ void WSWindowManager::invalidate(const WSWindow& window)
|
|||
invalidate(outerRectForWindow(window.rect()));
|
||||
}
|
||||
|
||||
void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect)
|
||||
{
|
||||
if (rect.is_empty()) {
|
||||
invalidate(window);
|
||||
return;
|
||||
}
|
||||
ASSERT_INTERRUPTS_ENABLED();
|
||||
LOCKER(m_lock);
|
||||
auto outer_rect = outerRectForWindow(window.rect());
|
||||
auto inner_rect = rect;
|
||||
inner_rect.move_by(window.position());
|
||||
inner_rect.intersect(outer_rect);
|
||||
invalidate(inner_rect);
|
||||
}
|
||||
|
||||
void WSWindowManager::flush(const Rect& a_rect)
|
||||
{
|
||||
auto rect = Rect::intersection(a_rect, m_screen_rect);
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
void draw_cursor();
|
||||
|
||||
void invalidate(const WSWindow&);
|
||||
void invalidate(const WSWindow&, const Rect&);
|
||||
void invalidate(const Rect&);
|
||||
void invalidate();
|
||||
void flush(const Rect&);
|
||||
|
|
Loading…
Add table
Reference in a new issue