diff --git a/Applications/Terminal/main.cpp b/Applications/Terminal/main.cpp index c15f2798c59..9ef6f64d83e 100644 --- a/Applications/Terminal/main.cpp +++ b/Applications/Terminal/main.cpp @@ -81,7 +81,7 @@ int main(int argc, char** argv) auto help_menu = make("?"); help_menu->add_item(2, "About"); - menubar->add_menu(move(app_menu)); + menubar->add_menu(move(help_menu)); app.set_menubar(move(menubar)); diff --git a/Kernel/Process.h b/Kernel/Process.h index 09741b84e21..b219cb6adba 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -230,6 +230,14 @@ public: int gui$get_window_rect(int window_id, GUI_Rect*); int gui$set_window_rect(int window_id, const GUI_Rect*); int gui$set_global_cursor_tracking_enabled(int window_id, bool enabled); + int gui$menubar_create(); + int gui$menubar_destroy(int menubar_id); + int gui$menubar_add_menu(int menubar_id, int menu_id); + int gui$menu_create(const char* name); + int gui$menu_destroy(int menu_id); + int gui$menu_add_separator(int menu_id); + int gui$menu_add_item(int menu_id, unsigned identifier, const char* text); + int gui$set_menubar(int menubar_id); DisplayInfo set_video_resolution(int width, int height); diff --git a/Kernel/ProcessGUI.cpp b/Kernel/ProcessGUI.cpp index 556a25199b7..da4e35b7369 100644 --- a/Kernel/ProcessGUI.cpp +++ b/Kernel/ProcessGUI.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include //#define LOG_GUI_SYSCALLS @@ -284,3 +285,48 @@ DisplayInfo Process::set_video_resolution(int width, int height) BochsVGADevice::the().set_resolution(width, height); return info; } + +int Process::gui$menubar_create() +{ + return WSWindowManager::the().api$menubar_create(); +} + +int Process::gui$menubar_destroy(int menubar_id) +{ + return WSWindowManager::the().api$menubar_destroy(menubar_id); +} + +int Process::gui$menubar_add_menu(int menubar_id, int menu_id) +{ + return WSWindowManager::the().api$menubar_add_menu(menubar_id, menu_id); +} + +int Process::gui$menu_create(const char* name) +{ + if (!validate_read_str(name)) + return -EFAULT; + return WSWindowManager::the().api$menu_create(String(name)); +} + +int Process::gui$menu_destroy(int menu_id) +{ + return WSWindowManager::the().api$menu_destroy(menu_id); +} + +int Process::gui$menu_add_separator(int menu_id) +{ + return WSWindowManager::the().api$menu_add_separator(menu_id); +} + +int Process::gui$menu_add_item(int menu_id, unsigned identifier, const char* text) +{ + if (!validate_read_str(text)) + return -EFAULT; + return WSWindowManager::the().api$menu_add_item(menu_id, identifier, String(text)); +} + +int Process::gui$set_menubar(int menubar_id) +{ + kprintf("gui$set_menubar %d\n", menubar_id); + return WSWindowManager::the().api$app_set_menubar(menubar_id); +} diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index a96a9de5363..6f18495bfd7 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -223,6 +223,22 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, return current->sys$rmdir((const char*)arg1); case Syscall::SC_chmod: return current->sys$chmod((const char*)arg1, (mode_t)arg2); + case Syscall::SC_gui_menubar_create: + return current->gui$menubar_create(); + case Syscall::SC_gui_menubar_destroy: + return current->gui$menubar_destroy((int)arg1); + case Syscall::SC_gui_menubar_add_menu: + return current->gui$menubar_add_menu((int)arg1, (int)arg2); + case Syscall::SC_gui_menu_create: + return current->gui$menu_create((const char*)arg1); + case Syscall::SC_gui_menu_destroy: + return current->gui$menu_destroy((int)arg1); + case Syscall::SC_gui_menu_add_separator: + return current->gui$menu_add_separator((int)arg1); + case Syscall::SC_gui_menu_add_item: + return current->gui$menu_add_item((int)arg1, (unsigned)arg2, (const char*)arg3); + case Syscall::SC_gui_app_set_menubar: + return current->gui$set_menubar((int)arg1); default: kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); break; diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 964426454d4..2e541278793 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -85,6 +85,14 @@ __ENUMERATE_SYSCALL(rmdir) \ __ENUMERATE_SYSCALL(chmod) \ __ENUMERATE_SYSCALL(usleep) \ + __ENUMERATE_SYSCALL(gui_menubar_create) \ + __ENUMERATE_SYSCALL(gui_menubar_destroy) \ + __ENUMERATE_SYSCALL(gui_menubar_add_menu) \ + __ENUMERATE_SYSCALL(gui_menu_create) \ + __ENUMERATE_SYSCALL(gui_menu_destroy) \ + __ENUMERATE_SYSCALL(gui_menu_add_separator) \ + __ENUMERATE_SYSCALL(gui_menu_add_item) \ + __ENUMERATE_SYSCALL(gui_app_set_menubar) \ #ifdef SERENITY diff --git a/LibC/errno_numbers.h b/LibC/errno_numbers.h index 3d5a80dde03..c43c863dc52 100644 --- a/LibC/errno_numbers.h +++ b/LibC/errno_numbers.h @@ -46,6 +46,8 @@ __ERROR(EBADWINDOW, "Bad window ID") \ __ERROR(EBADBACKING, "Bad backing store ID") \ __ERROR(ENOTEMPTY, "Directory not empty") \ + __ERROR(EBADMENUBAR, "Bad menubar ID") \ + __ERROR(EBADMENU, "Bad menu ID") \ enum __errno_values { diff --git a/LibC/gui.cpp b/LibC/gui.cpp index da1fecef052..0d9a07ed0a1 100644 --- a/LibC/gui.cpp +++ b/LibC/gui.cpp @@ -68,3 +68,51 @@ int gui_set_global_cursor_tracking_enabled(int window_id, bool enabled) int rc = syscall(SC_gui_set_global_cursor_tracking_enabled, window_id, enabled); __RETURN_WITH_ERRNO(rc, rc, -1); } + +int gui_menubar_create() +{ + int rc = syscall(SC_gui_menubar_create); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int gui_menubar_destroy(int menubar_id) +{ + int rc = syscall(SC_gui_menubar_destroy, menubar_id); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int gui_menubar_add_menu(int menubar_id, int menu_id) +{ + int rc = syscall(SC_gui_menubar_add_menu, menubar_id, menu_id); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int gui_menu_create(const char* name) +{ + int rc = syscall(SC_gui_menu_create, name); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int gui_menu_destroy(int menu_id) +{ + int rc = syscall(SC_gui_menu_destroy, menu_id); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int gui_menu_add_separator(int menu_id) +{ + int rc = syscall(SC_gui_menu_add_separator, menu_id); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int gui_menu_add_item(int menu_id, unsigned identifier, const char* text) +{ + int rc = syscall(SC_gui_menu_add_item, menu_id, identifier, text); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + +int gui_app_set_menubar(int menubar_id) +{ + int rc = syscall(SC_gui_app_set_menubar, menubar_id); + __RETURN_WITH_ERRNO(rc, rc, -1); +} diff --git a/LibC/gui.h b/LibC/gui.h index 4b7abbb88da..5cf4ff6b960 100644 --- a/LibC/gui.h +++ b/LibC/gui.h @@ -16,6 +16,14 @@ int gui_set_window_title(int window_id, const char*, size_t); int gui_get_window_rect(int window_id, GUI_Rect*); int gui_set_window_rect(int window_id, const GUI_Rect*); int gui_set_global_cursor_tracking_enabled(int window_id, bool); +int gui_menubar_create(); +int gui_menubar_destroy(int menubar_id); +int gui_menubar_add_menu(int menubar_id, int menu_id); +int gui_menu_create(const char* name); +int gui_menu_destroy(int menu_id); +int gui_menu_add_separator(int menu_id); +int gui_menu_add_item(int menu_id, unsigned identifier, const char* text); +int gui_app_set_menubar(int menubar_id); __END_DECLS diff --git a/LibGUI/GMenu.cpp b/LibGUI/GMenu.cpp index e73ad3b5793..de56467485b 100644 --- a/LibGUI/GMenu.cpp +++ b/LibGUI/GMenu.cpp @@ -1,4 +1,5 @@ #include +#include GMenu::GMenu(const String& name) : m_name(name) @@ -7,6 +8,10 @@ GMenu::GMenu(const String& name) GMenu::~GMenu() { + if (m_menu_id) { + gui_menu_destroy(m_menu_id); + m_menu_id = 0; + } } void GMenu::add_item(unsigned identifier, const String& text) @@ -18,3 +23,16 @@ void GMenu::add_separator() { m_items.append(GMenuItem(GMenuItem::Separator)); } + +int GMenu::realize_menu() +{ + m_menu_id = gui_menu_create(m_name.characters()); + ASSERT(m_menu_id > 0); + for (auto& item : m_items) { + if (item.type() == GMenuItem::Separator) + gui_menu_add_separator(m_menu_id); + else if (item.type() == GMenuItem::Text) + gui_menu_add_item(m_menu_id, item.identifier(), item.text().characters()); + } + return m_menu_id; +} diff --git a/LibGUI/GMenu.h b/LibGUI/GMenu.h index c96da58a6cb..55820495634 100644 --- a/LibGUI/GMenu.h +++ b/LibGUI/GMenu.h @@ -12,6 +12,11 @@ public: void add_separator(); private: + friend class GMenuBar; + int menu_id() const { return m_menu_id; } + int realize_menu(); + + int m_menu_id { 0 }; String m_name; Vector m_items; }; diff --git a/LibGUI/GMenuBar.cpp b/LibGUI/GMenuBar.cpp index d8a142fe4e7..25381e77cdc 100644 --- a/LibGUI/GMenuBar.cpp +++ b/LibGUI/GMenuBar.cpp @@ -1,4 +1,5 @@ #include +#include GMenuBar::GMenuBar() { @@ -15,8 +16,22 @@ void GMenuBar::add_menu(OwnPtr&& menu) void GMenuBar::notify_added_to_application(Badge) { + ASSERT(!m_menubar_id); + m_menubar_id = gui_menubar_create(); + ASSERT(m_menubar_id > 0); + for (auto& menu : m_menus) { + ASSERT(menu); + int menu_id = menu->realize_menu(); + ASSERT(menu_id > 0); + int rc = gui_menubar_add_menu(m_menubar_id, menu_id); + ASSERT(rc == 0); + } + gui_app_set_menubar(m_menubar_id); } void GMenuBar::notify_removed_from_application(Badge) { + ASSERT(m_menubar_id); + gui_menubar_destroy(m_menubar_id); + m_menubar_id = 0; } diff --git a/LibGUI/GMenuBar.h b/LibGUI/GMenuBar.h index 011a70f5963..6d556da89d3 100644 --- a/LibGUI/GMenuBar.h +++ b/LibGUI/GMenuBar.h @@ -18,5 +18,6 @@ public: void notify_removed_from_application(Badge); private: + int m_menubar_id { 0 }; Vector> m_menus; }; diff --git a/WindowServer/WSMenu.cpp b/WindowServer/WSMenu.cpp index 01fac116ea6..8d863712609 100644 --- a/WindowServer/WSMenu.cpp +++ b/WindowServer/WSMenu.cpp @@ -7,8 +7,9 @@ #include #include -WSMenu::WSMenu(const String& name) - : m_name(name) +WSMenu::WSMenu(int menu_id, String&& name) + : m_menu_id(menu_id) + , m_name(move(name)) { } diff --git a/WindowServer/WSMenu.h b/WindowServer/WSMenu.h index 99eef768a30..39c8b07dae3 100644 --- a/WindowServer/WSMenu.h +++ b/WindowServer/WSMenu.h @@ -12,9 +12,11 @@ class Font; class WSMenu { public: - WSMenu(const String& name); + WSMenu(int menu_id, String&& name); ~WSMenu(); + int menu_id() const { return m_menu_id; } + WSMenuBar* menu_bar() { return m_menubar; } const WSMenuBar* menu_bar() const { return m_menubar; } @@ -70,6 +72,7 @@ public: private: void did_activate(WSMenuItem&); + int m_menu_id { 0 }; String m_name; Rect m_rect_in_menubar; Rect m_text_rect_in_menubar; diff --git a/WindowServer/WSMenuBar.h b/WindowServer/WSMenuBar.h index 7cfc7baed42..fe177e16b8d 100644 --- a/WindowServer/WSMenuBar.h +++ b/WindowServer/WSMenuBar.h @@ -8,7 +8,7 @@ public: WSMenuBar(); ~WSMenuBar(); - void add_menu(OwnPtr&& menu) { m_menus.append(move(menu)); } + void add_menu(WSMenu* menu) { m_menus.append(menu); } template void for_each_menu(Callback callback) @@ -20,5 +20,5 @@ public: } private: - Vector> m_menus; + Vector m_menus; }; diff --git a/WindowServer/WSWindowManager.cpp b/WindowServer/WSWindowManager.cpp index f5f61492265..02cb835efbf 100644 --- a/WindowServer/WSWindowManager.cpp +++ b/WindowServer/WSWindowManager.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "WSMenu.h" #include "WSMenuBar.h" #include "WSMenuItem.h" @@ -185,28 +186,28 @@ WSWindowManager::WSWindowManager() { byte system_menu_name[] = { 0xfc, 0 }; - auto menu = make(String((const char*)system_menu_name)); - menu->add_item(make(0, "Launch Terminal")); - menu->add_item(make(WSMenuItem::Separator)); - menu->add_item(make(1, "Hello again")); - menu->add_item(make(2, "To all my friends")); - menu->add_item(make(3, "Together we can play some rock&roll")); - menu->add_item(make(WSMenuItem::Separator)); - menu->add_item(make(4, "About...")); - menu->on_item_activation = [] (WSMenuItem& item) { + auto& menu = create_menu(String((const char*)system_menu_name)); + menu.add_item(make(0, "Launch Terminal")); + menu.add_item(make(WSMenuItem::Separator)); + menu.add_item(make(1, "Hello again")); + menu.add_item(make(2, "To all my friends")); + menu.add_item(make(3, "Together we can play some rock&roll")); + menu.add_item(make(WSMenuItem::Separator)); + menu.add_item(make(4, "About...")); + menu.on_item_activation = [] (WSMenuItem& item) { kprintf("WSMenu 1 item activated: '%s'\n", item.text().characters()); }; - menubar->add_menu(move(menu)); + menubar->add_menu(&menu); } { - auto menu = make("Dummy"); - menu->add_item(make(5, "Foo.")); - menu->add_item(make(6, "Bar?")); - menu->add_item(make(7, "Baz!")); - menu->on_item_activation = [] (WSMenuItem& item) { + auto& menu = create_menu("Dummy"); + menu.add_item(make(5, "Foo.")); + menu.add_item(make(6, "Bar?")); + menu.add_item(make(7, "Baz!")); + menu.on_item_activation = [] (WSMenuItem& item) { kprintf("WSMenu 2 item activated: '%s'\n", item.text().characters()); }; - menubar->add_menu(move(menu)); + menubar->add_menu(&menu); } set_current_menubar(menubar.ptr()); @@ -223,11 +224,10 @@ WSWindowManager::~WSWindowManager() void WSWindowManager::set_current_menubar(WSMenuBar* menubar) { + LOCKER(m_lock); if (m_current_menubar == menubar) return; m_current_menubar = menubar; - if (!m_current_menubar) - return; int menu_margin = 16; Point next_menu_location { menu_margin / 2, 3 }; m_current_menubar->for_each_menu([&] (WSMenu& menu) { @@ -237,6 +237,7 @@ void WSWindowManager::set_current_menubar(WSMenuBar* menubar) next_menu_location.move_by(menu.rect_in_menubar().width(), 0); return true; }); + invalidate(); } static const char* s_close_button_bitmap_data = { @@ -445,6 +446,7 @@ void WSWindowManager::handle_close_button_mouse_event(WSWindow& window, WSMouseE void WSWindowManager::process_mouse_event(WSMouseEvent& event) { + LOCKER(m_lock); if (event.type() == WSMessage::MouseUp && event.button() == MouseButton::Left) { if (m_drag_window) { #ifdef DRAG_DEBUG @@ -770,3 +772,106 @@ void WSWindowManager::close_menu(WSMenu& menu) ASSERT(m_current_menu == &menu); close_current_menu(); } + +WSMenu& WSWindowManager::create_menu(String&& name) +{ + int menu_id = m_next_menu_id++; + auto menu = make(menu_id, move(name)); + auto* menu_ptr = menu.ptr(); + m_menus.set(menu_id, move(menu)); + return *menu_ptr; +} + + +int WSWindowManager::api$menubar_create() +{ + LOCKER(m_lock); + auto menubar = make(); + int menubar_id = m_next_menubar_id++; + m_menubars.set(menubar_id, move(menubar)); + return menubar_id; +} + +int WSWindowManager::api$menubar_destroy(int menubar_id) +{ + LOCKER(m_lock); + auto it = m_menubars.find(menubar_id); + if (it == m_menubars.end()) + return -EBADMENUBAR; + auto& menubar = *(*it).value; + if (&menubar == m_current_menubar) + ASSERT_NOT_REACHED(); + m_menubars.remove(it); + return 0; +} + +int WSWindowManager::api$menubar_add_menu(int menubar_id, int menu_id) +{ + LOCKER(m_lock); + auto it = m_menubars.find(menubar_id); + if (it == m_menubars.end()) + return -EBADMENUBAR; + auto& menubar = *(*it).value; + + auto jt = m_menus.find(menu_id); + if (jt == m_menus.end()) + return -EBADMENU; + auto& menu = *(*jt).value; + + menubar.add_menu(&menu); + return 0; +} + +int WSWindowManager::api$menu_create(String&& name) +{ + LOCKER(m_lock); + return create_menu(move(name)).menu_id(); +} + +int WSWindowManager::api$menu_destroy(int menu_id) +{ + LOCKER(m_lock); + auto it = m_menus.find(menu_id); + if (it == m_menus.end()) + return -EBADMENU; + m_menus.remove(it); + return 0; +} + +int WSWindowManager::api$menu_add_separator(int menu_id) +{ + LOCKER(m_lock); + auto it = m_menus.find(menu_id); + if (it == m_menus.end()) + return -EBADMENU; + auto& menu = *(*it).value; + menu.add_item(make(WSMenuItem::Separator)); + return 0; +} + +int WSWindowManager::api$menu_add_item(int menu_id, unsigned identifier, String&& text) +{ + LOCKER(m_lock); + auto it = m_menus.find(menu_id); + if (it == m_menus.end()) + return -EBADMENU; + auto& menu = *(*it).value; + menu.add_item(make(identifier, move(text))); + return 0; +} + +int WSWindowManager::api$app_set_menubar(int menubar_id) +{ + LOCKER(m_lock); + auto it = m_menubars.find(menubar_id); + if (it == m_menubars.end()) + return -EBADMENUBAR; + auto& menubar = *(*it).value; + if (&menubar == m_current_menubar) + return 0; + set_current_menubar(&menubar); + // FIXME: Maybe leave the system menu even if the app menu changes? + close_current_menu(); + invalidate(); + return 0; +} diff --git a/WindowServer/WSWindowManager.h b/WindowServer/WSWindowManager.h index 46641b91ba9..3b8e96e43c3 100644 --- a/WindowServer/WSWindowManager.h +++ b/WindowServer/WSWindowManager.h @@ -53,6 +53,15 @@ public: void close_menu(WSMenu&); + int api$menubar_create(); + int api$menubar_destroy(int menubar_id); + int api$menubar_add_menu(int menubar_id, int menu_id); + int api$menu_create(String&&); + int api$menu_destroy(int menu_id); + int api$menu_add_separator(int menu_id); + int api$menu_add_item(int menu_id, unsigned identifier, String&& text); + int api$app_set_menubar(int menubar_id); + private: WSWindowManager(); virtual ~WSWindowManager() override; @@ -66,6 +75,7 @@ private: void set_active_window(WSWindow*); void close_current_menu(); + WSMenu& create_menu(String&& name); virtual void on_message(WSMessage&) override; @@ -128,7 +138,11 @@ private: Lockable m_flash_flush; bool m_buffers_are_flipped { false }; + int m_next_menubar_id = 100; + int m_next_menu_id = 900; + WSMenuBar* m_current_menubar { nullptr }; WSMenu* m_current_menu { nullptr }; HashMap> m_menubars; + HashMap> m_menus; };