redesign compositor protocol, start widget library
This commit is contained in:
		
							parent
							
								
									be691582ee
								
							
						
					
					
						commit
						e6c3a80b01
					
				
					 24 changed files with 526 additions and 251 deletions
				
			
		|  | @ -8,114 +8,95 @@ | |||
| struct socket_state { | ||||
| 
 | ||||
|   euler::syscall::stream_handle socket; | ||||
|   std::vector<window *> windows; | ||||
|   daguerre::hilbert_color window_bg = euler::syscall::encode_color(0, 0, 0); | ||||
|   window *w; | ||||
| 
 | ||||
|   bool try_open_window() { | ||||
|   bool try_show_window() { | ||||
| 
 | ||||
|     struct [[gnu::packed]] { | ||||
|       uint32_t width; | ||||
|       uint32_t height; | ||||
|     } body; | ||||
| 
 | ||||
|     if (euler::syscall::read_from_stream(socket, sizeof(body), &body) != | ||||
|         euler::syscall::stream_result::success) | ||||
|     if (w->is_shown) | ||||
|       return false; | ||||
| 
 | ||||
|     window *w = new window(body.width, body.height); | ||||
|     w->contents.fill(window_bg); | ||||
| 
 | ||||
|     uint16_t wid = 0; | ||||
| 
 | ||||
|     while (wid < windows.size()) | ||||
|       if (windows[wid] != 0) | ||||
|         ++wid; | ||||
|       else | ||||
|         break; | ||||
| 
 | ||||
|     if (wid == windows.size()) | ||||
|       windows.push_back(w); | ||||
|     else | ||||
|       windows[wid] = w; | ||||
| 
 | ||||
|     r->lock(); | ||||
|     r->add_window(w); | ||||
|     r->unlock(); | ||||
| 
 | ||||
|     struct [[gnu::packed]] { | ||||
|       uint8_t type; | ||||
|       uint16_t the_window; | ||||
|     } response { | ||||
|       .type = 0x00, | ||||
|       .the_window = wid | ||||
|     }; | ||||
| 
 | ||||
|     return | ||||
|       euler::syscall::write_to_stream(socket, sizeof(response), &response) == | ||||
|       euler::syscall::stream_result::success; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   bool try_update_window_region() { | ||||
| 
 | ||||
|     struct [[gnu::packed]] { | ||||
|       uint16_t window; | ||||
|       uint32_t start_x; | ||||
|       uint32_t start_y; | ||||
|       uint32_t width; | ||||
|       uint32_t height; | ||||
|     } body_head; | ||||
| 
 | ||||
|     if (euler::syscall::read_from_stream(socket, sizeof(body_head), &body_head) != | ||||
|         euler::syscall::stream_result::success) | ||||
|       return false; | ||||
| 
 | ||||
|     std::vector<daguerre::hilbert_color> data(body_head.width * body_head.height); | ||||
| 
 | ||||
|     if (euler::syscall::read_from_stream(socket, data.size() * 4, data.data()) != | ||||
|         euler::syscall::stream_result::success) | ||||
|       return false; | ||||
| 
 | ||||
|     daguerre::image<daguerre::hilbert_color> | ||||
|       data_as_image(body_head.width, body_head.height, data.data(), body_head.width, false); | ||||
| 
 | ||||
|     if (body_head.window >= windows.size() || !windows[body_head.window]) | ||||
|       return false; | ||||
| 
 | ||||
|     window *w = windows[body_head.window]; | ||||
| 
 | ||||
|     r->lock(); | ||||
| 
 | ||||
|     if ((int)body_head.start_x + data_as_image.width > w->contents.width || | ||||
|         (int)body_head.start_y + data_as_image.height > w->contents.height) { | ||||
|       r->unlock(); | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     w->contents.copy_from(data_as_image, body_head.start_x, body_head.start_y); | ||||
| 
 | ||||
|     r->unlock(); | ||||
|     r->dispatch_render(); | ||||
|     return true; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   bool try_close_window() { | ||||
|   bool try_hide_window() { | ||||
| 
 | ||||
|     uint16_t wid; | ||||
| 
 | ||||
|     if (euler::syscall::read_from_stream(socket, 2, &wid) != | ||||
|         euler::syscall::stream_result::success) | ||||
|       return false; | ||||
| 
 | ||||
|     if (wid >= windows.size() || !windows[wid]) | ||||
|     if (!w->is_shown) | ||||
|       return false; | ||||
| 
 | ||||
|     r->lock(); | ||||
|     r->remove_window(w); | ||||
|     r->unlock(); | ||||
|     r->dispatch_render(); | ||||
|     return true; | ||||
| 
 | ||||
|     r->remove_window(windows[wid]); | ||||
|     windows[wid] = 0; | ||||
|   } | ||||
| 
 | ||||
|   bool try_set_window_dimensions() { | ||||
| 
 | ||||
|     struct [[gnu::packed]] { uint32_t width; uint32_t height; } packet; | ||||
|     if (euler::syscall::read_from_stream(socket, sizeof(packet), &packet) != | ||||
|         euler::syscall::stream_result::success) | ||||
|       return false; | ||||
| 
 | ||||
|     if (packet.width > __INT_MAX__ || packet.height > __INT_MAX__) | ||||
|       return false; | ||||
| 
 | ||||
|     r->lock(); | ||||
|     w->contents = daguerre::image<daguerre::hilbert_color>( | ||||
|       packet.width, packet.height); | ||||
|     r->unlock(); | ||||
|     return true; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   bool try_set_window_title() { | ||||
| 
 | ||||
|     uint32_t length; | ||||
|     if (euler::syscall::read_from_stream(socket, 4, &length) != | ||||
|         euler::syscall::stream_result::success) | ||||
|       return false; | ||||
| 
 | ||||
|     std::string title; | ||||
|     title.resize(length); | ||||
|     if (euler::syscall::read_from_stream(socket, length, title.data()) != | ||||
|         euler::syscall::stream_result::success) | ||||
|       return false; | ||||
| 
 | ||||
|     r->lock(); | ||||
|     w->title = std::move(title); | ||||
|     r->unlock(); | ||||
|     r->dispatch_render(); | ||||
|     return true; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   bool try_update_window_region() { | ||||
| 
 | ||||
|     struct [[gnu::packed]] { | ||||
|       uint32_t start_x; uint32_t start_y; uint32_t width; uint32_t height; | ||||
|     } packet; | ||||
|     if (euler::syscall::read_from_stream(socket, sizeof(packet), &packet) != | ||||
|         euler::syscall::stream_result::success) | ||||
|       return false; | ||||
| 
 | ||||
|     static_assert(__INT_MAX__ <= __UINT64_MAX__); | ||||
|     if ((uint64_t)packet.start_x + packet. width > (uint64_t)w->contents. width || | ||||
|         (uint64_t)packet.start_y + packet.height > (uint64_t)w->contents.height) | ||||
|       return false; | ||||
| 
 | ||||
|     daguerre::image<daguerre::hilbert_color> content(packet.width, packet.height); | ||||
|     if (euler::syscall::read_from_stream( | ||||
|           socket, packet.width * packet.height * 4, content.buffer) != | ||||
|         euler::syscall::stream_result::success) | ||||
|       return false; | ||||
| 
 | ||||
|     r->lock(); | ||||
|     w->contents.copy_from(content, packet.start_x, packet.start_y); | ||||
|     r->unlock(); | ||||
|     r->dispatch_render(); | ||||
|     return true; | ||||
|  | @ -130,9 +111,11 @@ struct socket_state { | |||
|       return false; | ||||
| 
 | ||||
|     switch (type) { | ||||
|     case 0x00: return try_open_window(); | ||||
|     case 0x01: return try_update_window_region(); | ||||
|     case 0x02: return try_close_window(); | ||||
|     case 0x00: return try_show_window(); | ||||
|     case 0x01: return try_hide_window(); | ||||
|     case 0x02: return try_set_window_dimensions(); | ||||
|     case 0x03: return try_set_window_title(); | ||||
|     case 0x04: return try_update_window_region(); | ||||
|     default: return false; | ||||
|     } | ||||
| 
 | ||||
|  | @ -144,18 +127,20 @@ struct socket_state { | |||
| 
 | ||||
|   euler::syscall::set_thread_name("socket thread"); | ||||
| 
 | ||||
|   window *w = new window(); | ||||
|   socket_state *state = new socket_state { | ||||
|     .socket = socket, .windows = {} }; | ||||
|     .socket = socket, .w = w }; | ||||
| 
 | ||||
|   while (state->try_process_request()) ; | ||||
| 
 | ||||
|   r->lock(); | ||||
|   for (unsigned i = 0; i < state->windows.size(); ++i) { | ||||
|     r->remove_window(state->windows[i]); | ||||
|     delete state->windows[i]; | ||||
|   if (w->is_shown) { | ||||
|     r->lock(); | ||||
|     r->remove_window(w); | ||||
|     r->unlock(); | ||||
|   } | ||||
|   r->unlock(); | ||||
| 
 | ||||
|   delete state; | ||||
|   delete w; | ||||
|   euler::syscall::close_stream(socket); | ||||
|   euler::syscall::end_this_thread(0); | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,6 +9,10 @@ struct window { | |||
|   int x; | ||||
|   int y; | ||||
| 
 | ||||
|   window(int width, int height) : contents(width, height), x(0), y(0) {} | ||||
|   bool is_shown; | ||||
| 
 | ||||
|   std::string title; | ||||
| 
 | ||||
|   window() : x(0), y(0), is_shown(false) {} | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ build/%.cpp.o: source/%.cpp | |||
| 	$(HILBERT_CC) -c $^ -o $@ | ||||
| 
 | ||||
| build/hello.elf: $(SOURCES:%=build/%.o) | ||||
| 	$(HILBERT_CC) $^ -ldaguerre -o $@ | ||||
| 	$(HILBERT_CC) $^ -ldaguerre -lpake -o $@ | ||||
| 
 | ||||
| clean: | ||||
| 	rm -rf build | ||||
|  |  | |||
|  | @ -1,37 +1,30 @@ | |||
| #include <goldman/protocol.hpp> | ||||
| #include <pake/widgets/fixed-text.hpp> | ||||
| #include <daguerre/psf.hpp> | ||||
| #include <pake/window.hpp> | ||||
| 
 | ||||
| template <class color_t> | ||||
| void overlay(color_t &to, const bool &from, const color_t ¶m) { | ||||
|   if (from) | ||||
|     to = param; | ||||
| } | ||||
| daguerre::fixed_font<bool> *font; | ||||
| 
 | ||||
| int main(int, char **) { | ||||
| 
 | ||||
|   auto bg = euler::syscall::encode_color(0xaa, 0xaa, 0xaa); | ||||
|   auto fg = euler::syscall::encode_color(0x00, 0x00, 0x00); | ||||
|   font = new daguerre::fixed_font<bool>( | ||||
|     daguerre::try_load_psf("/assets/terminus-bold-18x10.psf").value()); | ||||
| 
 | ||||
|   daguerre::image<daguerre::hilbert_color> image(300, 200); | ||||
|   image.fill(bg); | ||||
|   pake::widgets::fixed_text *text = | ||||
|     new pake::widgets::fixed_text("Hello, world!", font, | ||||
|       euler::syscall::encode_color(0xaa, 0xaa, 0xaa), | ||||
|       euler::syscall::encode_color(0x00, 0x00, 0x00)); | ||||
| 
 | ||||
|   auto font = daguerre::try_load_psf("/assets/terminus-bold-18x10.psf"); | ||||
|   image.render_text(*font, fg, 10, 10, "Hello, world!", &overlay); | ||||
|   pake::window w(300, 200, "Hello"); | ||||
|   w.set_root(std::unique_ptr<pake::widget>(text)); | ||||
|   w.render_and_send_to_compositor(); | ||||
|   w.show(); | ||||
| 
 | ||||
|   euler::syscall::stream_handle s; | ||||
|   euler::syscall::connect_to_socket("hilbert.compositor", s); | ||||
| 
 | ||||
|   goldman::protocol::send_open_window(s, 300, 200); | ||||
|   //TODO: call event loop
 | ||||
| 
 | ||||
|   euler::syscall::stream_handle h1, h2; | ||||
|   euler::syscall::create_private_socket(h1, h2); | ||||
|   uint8_t byte; | ||||
|   euler::syscall::read_from_stream(s, 1, &byte); | ||||
| 
 | ||||
|   auto w = goldman::protocol::get_window_opened_body(s); | ||||
| 
 | ||||
|   goldman::protocol::send_update_window_region( | ||||
|     s, w, 0, 0, 300, 200, image.buffer, image.buffer_pitch); | ||||
| 
 | ||||
|   euler::syscall::read_from_stream(s, 1, &byte); | ||||
|   __builtin_unreachable(); | ||||
|   while (1) | ||||
|     euler::syscall::read_from_stream(h1, 1, &byte); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,9 @@ | |||
| compositors listen on the socket id "hilbert.compositor". | ||||
| 
 | ||||
| when a window is opened by an application, that window can only be referred to | ||||
| on that socket. the opaque value given in the "window opened" message refers to | ||||
| that window in future messages on that socket. it is guaranteed to be distinct | ||||
| for different windows on the same socket, and in no way guaranteed to be | ||||
| distinct for different windows on different sockets. the window is bound | ||||
| just to the socket, not to the application. if the socket where a window | ||||
| was created is gifted to a new process, the new process has complete control | ||||
| over the window, and the compositor does not need to be informed. | ||||
| there is a one-to-one correspondence between sockets to the compositor and | ||||
| windows. when a socket is opened, a window is created, and when a socket is | ||||
| closed, its window is destroyed. this socket can be gifted to another process, | ||||
| and the other process then becomes the window's owner. | ||||
| 
 | ||||
| data types: | ||||
| 
 | ||||
|  | @ -18,32 +14,28 @@ data types: | |||
|   color rectangle: | ||||
|     multiple hilbert colors, top to bottom by row, left to right within row | ||||
| 
 | ||||
|   window: | ||||
|     opaque word (given in "window opened" message after "open window" message) | ||||
| 
 | ||||
| messages from applications to compositor: | ||||
| 
 | ||||
|   open window: | ||||
|   show window: | ||||
|     byte: 0x00 | ||||
|     dword: window width | ||||
|     dword: window height | ||||
| 
 | ||||
|   hide window: | ||||
|     byte: 0x01 | ||||
| 
 | ||||
|   set window dimensions: | ||||
|     byte: 0x02 | ||||
|     dword: width | ||||
|     dword: height | ||||
| 
 | ||||
|   set window title: | ||||
|     byte: 0x03 | ||||
|     dword: length in bytes | ||||
|     bytes: title | ||||
| 
 | ||||
|   update window region: | ||||
|     byte: 0x01 | ||||
|     window: the window | ||||
|     byte: 0x04 | ||||
|     dword: start x | ||||
|     dword: start y | ||||
|     dword: width | ||||
|     dword: height | ||||
|     color rectangle: the data | ||||
| 
 | ||||
|   close window: | ||||
|     byte: 0x02 | ||||
|     window: the window | ||||
| 
 | ||||
| messages from compositor to application: | ||||
| 
 | ||||
|   window opened: | ||||
|     byte: 0x00 | ||||
|     window: the window | ||||
|     these come in the order the open window requests were received | ||||
|     color rectangle: new content | ||||
|  |  | |||
							
								
								
									
										12
									
								
								euler/include/cassert
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								euler/include/cassert
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| #pragma once | ||||
| 
 | ||||
| namespace euler { | ||||
| 
 | ||||
|   [[noreturn]] inline void assert_failed() { | ||||
|     //TODO: log error and abort | ||||
|     while (1) ; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #define assert(cond) ((cond) ? (void)0 : ::euler::assert_failed()); | ||||
							
								
								
									
										3
									
								
								euler/include/condition_variable
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								euler/include/condition_variable
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <std/condition-variable.hpp> | ||||
|  | @ -1,4 +1,4 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <std/unique_lock.hpp> | ||||
| #include <std/unique-lock.hpp> | ||||
| #include <std/mutex.hpp> | ||||
|  |  | |||
							
								
								
									
										13
									
								
								euler/include/std/condition-variable.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								euler/include/std/condition-variable.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <mutex> | ||||
| 
 | ||||
| namespace std { | ||||
| 
 | ||||
|   class condition_variable { | ||||
| 
 | ||||
|     //TODO
 | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
|  | @ -19,11 +19,11 @@ namespace std { | |||
| 
 | ||||
|       node *the_node; | ||||
| 
 | ||||
|       bool operator ==(const generic_iterator &other) { | ||||
|       bool operator ==(const generic_iterator &other) const { | ||||
|         return the_node == other.the_node; | ||||
|       } | ||||
| 
 | ||||
|       bool operator !=(const generic_iterator &other) { | ||||
|       bool operator !=(const generic_iterator &other) const { | ||||
|         return the_node != other.the_node; | ||||
|       } | ||||
| 
 | ||||
|  | @ -82,13 +82,14 @@ namespace std { | |||
|       return iterator { .the_node = r }; | ||||
|     } | ||||
| 
 | ||||
|     iterator begin() const noexcept { | ||||
|       return iterator { .the_node = first_node }; | ||||
|     } | ||||
|     iterator begin() noexcept { return iterator { .the_node = first_node }; } | ||||
|     iterator   end() noexcept { return iterator { .the_node = 0 }; } | ||||
| 
 | ||||
|     iterator end() const noexcept { | ||||
|       return iterator { .the_node = 0 }; | ||||
|     } | ||||
|     const_iterator begin() const noexcept { return iterator { .the_node = first_node }; } | ||||
|     const_iterator   end() const noexcept { return iterator { .the_node = 0 }; } | ||||
| 
 | ||||
|     const_iterator cbegin() const noexcept { return iterator { .the_node = first_node }; } | ||||
|     const_iterator   cend() const noexcept { return iterator { .the_node = 0 }; } | ||||
| 
 | ||||
|     size_t remove(const T &value) { | ||||
|       size_t removed = 0; | ||||
|  | @ -140,6 +141,7 @@ namespace std { | |||
|       clear(); | ||||
|       for (node *n = other.first_node; n; n = n->next) | ||||
|         push_back(n->value); | ||||
|       return *this; | ||||
|     } | ||||
| 
 | ||||
|     list &operator =(list &&other) { | ||||
|  | @ -150,6 +152,7 @@ namespace std { | |||
|       other.first_node = 0; | ||||
|       other.last_node = 0; | ||||
|       other.count = 0; | ||||
|       return *this; | ||||
|     } | ||||
| 
 | ||||
|   }; | ||||
|  |  | |||
|  | @ -131,6 +131,12 @@ namespace std { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     void clear() { | ||||
|       for (size_type i = 0; i < _size; ++i) | ||||
|         std::destroy_at(_data + i); | ||||
|       _size = 0; | ||||
|     } | ||||
| 
 | ||||
|     constexpr size_type size() const noexcept { | ||||
|       return _size; | ||||
|     } | ||||
|  | @ -188,6 +194,18 @@ namespace std { | |||
|       ++_size; | ||||
|     } | ||||
| 
 | ||||
|     using iterator = T *; | ||||
|     using const_iterator = const T *; | ||||
| 
 | ||||
|     iterator begin() noexcept { return _data; } | ||||
|     iterator   end() noexcept { return _data + _size; } | ||||
| 
 | ||||
|     const_iterator begin() const noexcept { return _data; } | ||||
|     const_iterator   end() const noexcept { return _data + _size; } | ||||
| 
 | ||||
|     const_iterator cbegin() const noexcept { return _data; } | ||||
|     const_iterator   cend() const noexcept { return _data + _size; } | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -55,6 +55,8 @@ namespace daguerre { | |||
|     ~image(); | ||||
| 
 | ||||
|     void fill(const color_t &color); | ||||
|     void fill( | ||||
|       const color_t &color, int start_x, int start_y, int width, int height); | ||||
| 
 | ||||
|     //does not check bounds
 | ||||
|     color_t &at(int x, int y); | ||||
|  |  | |||
|  | @ -103,6 +103,14 @@ namespace daguerre { | |||
|         buffer[y * buffer_pitch + x] = color; | ||||
|   } | ||||
| 
 | ||||
|   template <class color_t> | ||||
|   void image<color_t>::fill( | ||||
|     const color_t &color, int start_x, int start_y, int width, int height) { | ||||
|     for (int y = start_y; y < start_y + height; ++y) | ||||
|       for (int x = 0; x < start_x + width; ++x) | ||||
|         buffer[y * buffer_pitch + x] = color; | ||||
|   } | ||||
| 
 | ||||
|   template <class color_t> | ||||
|   color_t &image<color_t>::at(int x, int y) { | ||||
|     return buffer[y * buffer_pitch + x]; | ||||
|  |  | |||
|  | @ -1,85 +0,0 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <euler/syscall.hpp> | ||||
| #include <memory> | ||||
| 
 | ||||
| //TODO: handle stream errors, make thread safe
 | ||||
| 
 | ||||
| namespace goldman::protocol { | ||||
| 
 | ||||
|   typedef euler::syscall::encoded_color color; | ||||
|   typedef uint16_t window; | ||||
| 
 | ||||
|   static inline void send_open_window( | ||||
|     euler::syscall::stream_handle socket, uint32_t width, uint32_t height) { | ||||
| 
 | ||||
|     struct [[gnu::packed]] { | ||||
|       uint8_t type; | ||||
|       uint32_t width; | ||||
|       uint32_t height; | ||||
|     } packet { | ||||
|       .type = 0x00, | ||||
|       .width = width, | ||||
|       .height = height | ||||
|     }; | ||||
| 
 | ||||
|     euler::syscall::write_to_stream(socket, sizeof(packet), &packet); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void send_update_window_region( | ||||
|     euler::syscall::stream_handle socket, window the_window, | ||||
|     uint32_t start_x, uint32_t start_y, uint32_t width, | ||||
|     uint32_t height, const color *the_data, size_t data_pitch) { | ||||
| 
 | ||||
|     struct [[gnu::packed]] { | ||||
|       uint8_t type; | ||||
|       window the_window; | ||||
|       uint32_t start_x; | ||||
|       uint32_t start_y; | ||||
|       uint32_t width; | ||||
|       uint32_t height; | ||||
|     } packet_head { | ||||
|       .type = 0x01, | ||||
|       .the_window = the_window, | ||||
|       .start_x = start_x, | ||||
|       .start_y = start_y, | ||||
|       .width = width, | ||||
|       .height = height | ||||
|     }; | ||||
| 
 | ||||
|     euler::syscall::write_to_stream(socket, sizeof(packet_head), &packet_head); | ||||
|     for (uint32_t y = 0; y < height; ++y) | ||||
|       euler::syscall::write_to_stream( | ||||
|         socket, width * sizeof(color), the_data + data_pitch * y); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void send_close_window( | ||||
|     euler::syscall::stream_handle socket, window the_window) { | ||||
| 
 | ||||
|     struct [[gnu::packed]] { | ||||
|       uint8_t type; | ||||
|       window the_window; | ||||
|     } packet { | ||||
|       .type = 0x02, | ||||
|       .the_window = the_window | ||||
|     }; | ||||
| 
 | ||||
|     euler::syscall::write_to_stream(socket, sizeof(packet), &packet); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   enum class response_id : uint8_t { | ||||
|     window_opened | ||||
|   }; | ||||
| 
 | ||||
|   window get_window_opened_body(euler::syscall::stream_handle socket) { | ||||
| 
 | ||||
|     window w; | ||||
|     euler::syscall::read_from_stream(socket, sizeof(w), &w); | ||||
|     return w; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										27
									
								
								libraries/pake/include/pake/dirtiable-image.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								libraries/pake/include/pake/dirtiable-image.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <daguerre/image.hpp> | ||||
| #include <list> | ||||
| 
 | ||||
| namespace pake { | ||||
| 
 | ||||
|   struct region { | ||||
|     int start_x; | ||||
|     int start_y; | ||||
|     int width; | ||||
|     int height; | ||||
|   }; | ||||
| 
 | ||||
|   struct dirtiable_image { | ||||
| 
 | ||||
|     daguerre::image<daguerre::hilbert_color> image; | ||||
|     daguerre::image<bool> dirty; | ||||
| 
 | ||||
|     std::vector<region> get_dirty_regions(); | ||||
|     void clear_dirty(); | ||||
| 
 | ||||
|     dirtiable_image(int width, int height); | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										19
									
								
								libraries/pake/include/pake/widget.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								libraries/pake/include/pake/widget.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <pake/dirtiable-image.hpp> | ||||
| 
 | ||||
| namespace pake { | ||||
| 
 | ||||
|   class widget { | ||||
| 
 | ||||
|   public: | ||||
|     virtual ~widget() {} | ||||
| 
 | ||||
|     virtual void render( | ||||
|       dirtiable_image &onto, int x_off, int y_off, bool force) = 0; | ||||
| 
 | ||||
|     virtual void notify_size(int width, int height) = 0; | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										31
									
								
								libraries/pake/include/pake/widgets/fixed-text.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								libraries/pake/include/pake/widgets/fixed-text.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <daguerre/fixed-font.hpp> | ||||
| #include <pake/widget.hpp> | ||||
| 
 | ||||
| namespace pake::widgets { | ||||
| 
 | ||||
|   class fixed_text : public widget { | ||||
| 
 | ||||
|     const daguerre::fixed_font<bool> *font; | ||||
|     daguerre::hilbert_color bg, fg; | ||||
|     std::string text; | ||||
| 
 | ||||
|     int width, height; | ||||
| 
 | ||||
|   public: | ||||
|     //TODO: look up font in some kind of catalogue
 | ||||
|     fixed_text( | ||||
|       std::string &&text, | ||||
|       const daguerre::fixed_font<bool> *font, | ||||
|       daguerre::hilbert_color bg, | ||||
|       daguerre::hilbert_color fg); | ||||
| 
 | ||||
|     virtual void render( | ||||
|       dirtiable_image &onto, int x_off, int y_off, bool force) override; | ||||
| 
 | ||||
|     virtual void notify_size(int width, int height) override; | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										32
									
								
								libraries/pake/include/pake/window.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								libraries/pake/include/pake/window.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <pake/dirtiable-image.hpp> | ||||
| #include <pake/widget.hpp> | ||||
| #include <memory> | ||||
| 
 | ||||
| namespace pake { | ||||
| 
 | ||||
|   class window { | ||||
| 
 | ||||
|     euler::syscall::stream_handle socket; | ||||
| 
 | ||||
|     int width; | ||||
|     int height; | ||||
| 
 | ||||
|     dirtiable_image contents; | ||||
|     std::unique_ptr<widget> root; | ||||
| 
 | ||||
|   public: | ||||
|     window(int width, int height, const std::string &title); | ||||
|     ~window(); | ||||
| 
 | ||||
|     void show(); | ||||
|     void hide(); | ||||
| 
 | ||||
|     void set_root(std::unique_ptr<widget> &&w); | ||||
| 
 | ||||
|     void render_and_send_to_compositor(); | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										12
									
								
								libraries/pake/makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								libraries/pake/makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| SOURCES = \
 | ||||
|   widgets/fixed-text.cpp dirtiable-image.cpp window.cpp | ||||
| 
 | ||||
| build/%.cpp.o: source/%.cpp | ||||
| 	@mkdir -p $(@D) | ||||
| 	$(HILBERT_CC) -c $^ -o $@ | ||||
| 
 | ||||
| build/libpake.a: $(SOURCES:%=build/%.o) | ||||
| 	$(HILBERT_AR) rcs $@ $^ | ||||
| 
 | ||||
| clean: | ||||
| 	rm -rf build | ||||
							
								
								
									
										80
									
								
								libraries/pake/source/dirtiable-image.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								libraries/pake/source/dirtiable-image.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,80 @@ | |||
| #include <pake/dirtiable-image.hpp> | ||||
| 
 | ||||
| namespace pake { | ||||
| 
 | ||||
|   struct dirty_region_builder { | ||||
| 
 | ||||
|     std::vector<region> regions_not_on_bottom; | ||||
|     std::list<region> regions_on_bottom; | ||||
| 
 | ||||
|     void add_row(const std::vector<region> &row) { | ||||
| 
 | ||||
|       std::list<region> new_regions_on_bottom; | ||||
| 
 | ||||
|       for (auto i = row.begin(); i < row.end(); ++i) { | ||||
|         bool expanded = false; | ||||
|         for (auto j = regions_on_bottom.begin(); j != regions_on_bottom.end(); ++j) | ||||
|           if (i->start_x == j->start_x && i->width == j->width) { | ||||
|             j->height += i->height; | ||||
|             new_regions_on_bottom.push_back(*j); | ||||
|             regions_on_bottom.erase(j); | ||||
|             expanded = true; | ||||
|             break; | ||||
|           } | ||||
|         if (!expanded) | ||||
|           new_regions_on_bottom.push_back(*i); | ||||
|       } | ||||
| 
 | ||||
|       for (auto i = regions_on_bottom.begin(); i != regions_on_bottom.end(); ++i) | ||||
|         regions_not_on_bottom.push_back(*i); | ||||
| 
 | ||||
|       regions_on_bottom = std::move(new_regions_on_bottom); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
|   std::vector<region> dirtiable_image::get_dirty_regions() { | ||||
| 
 | ||||
|     dirty_region_builder builder; | ||||
| 
 | ||||
|     std::vector<region> row; | ||||
| 
 | ||||
|     for (int y = 0; y < dirty.height; ++y) { | ||||
| 
 | ||||
|       int r = 0; | ||||
|       for (int x = 0; x < dirty.width; ++x) | ||||
|         if (!dirty.at(x, y)) { | ||||
|           if (r != x) | ||||
|             row.push_back({ | ||||
|               .start_x = r, .start_y = y, | ||||
|               .width = x - r, .height = 1 | ||||
|             }); | ||||
|           r = x + 1; | ||||
|         } | ||||
|       if (r != dirty.width) | ||||
|         row.push_back({ | ||||
|           .start_x = r, .start_y = y, | ||||
|           .width = dirty.width - r, .height = 1 | ||||
|         }); | ||||
| 
 | ||||
|       builder.add_row(row); | ||||
|       row.clear(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     builder.add_row(row); | ||||
|     return builder.regions_not_on_bottom; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void dirtiable_image::clear_dirty() { | ||||
|     dirty.fill(false); | ||||
|   } | ||||
| 
 | ||||
|   dirtiable_image::dirtiable_image(int width, int height) | ||||
|   : image(width, height), dirty(width, height) { | ||||
|     dirty.fill(false); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										41
									
								
								libraries/pake/source/widgets/fixed-text.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								libraries/pake/source/widgets/fixed-text.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| #include <pake/widgets/fixed-text.hpp> | ||||
| 
 | ||||
| static void draw_if_true( | ||||
|   daguerre::hilbert_color &out, const bool &in, | ||||
|   const daguerre::hilbert_color ¶m) { | ||||
|   if (in) out = param; | ||||
| } | ||||
| 
 | ||||
| namespace pake::widgets { | ||||
| 
 | ||||
|   fixed_text::fixed_text( | ||||
|     std::string &&text, | ||||
|     const daguerre::fixed_font<bool> *font, | ||||
|     daguerre::hilbert_color bg, | ||||
|     daguerre::hilbert_color fg) | ||||
|   : font(font), bg(bg), fg(fg), text(std::move(text)) {} | ||||
| 
 | ||||
|   void fixed_text::render( | ||||
|     dirtiable_image &onto, int x_off, int y_off, bool force) { | ||||
| 
 | ||||
|     if (force) { | ||||
|       onto.image.fill(  bg, x_off, y_off, width, height); | ||||
|       onto.dirty.fill(true, x_off, y_off, width, height); | ||||
|       //TODO: have options for alignment
 | ||||
|       //TODO: check overflow
 | ||||
|       onto.image.render_text( | ||||
|         *font, fg, x_off, y_off, | ||||
|         text.data(), draw_if_true); | ||||
|       onto.dirty.fill( | ||||
|         true, x_off, y_off, | ||||
|         font->glyph_width * text.size(), | ||||
|         font->glyph_height); | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   void fixed_text::notify_size(int width, int height) { | ||||
|     this->width = width; this->height = height; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										78
									
								
								libraries/pake/source/window.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								libraries/pake/source/window.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | |||
| #include <pake/window.hpp> | ||||
| #include <cassert> | ||||
| 
 | ||||
| //TODO: handle errors on socket connection, read, and write
 | ||||
| 
 | ||||
| namespace pake { | ||||
| 
 | ||||
|   window::window(int width, int height, const std::string &title) | ||||
|   : width(width), height(height), contents(width, height), root() { | ||||
| 
 | ||||
|     euler::syscall::connect_to_socket("hilbert.compositor", socket); | ||||
| 
 | ||||
|     assert(width >= 0 && height >= 0); | ||||
| 
 | ||||
|     struct [[gnu::packed]] { uint8_t type; uint32_t width; uint32_t height; } | ||||
|       dimensions_pkt = {.type = 0x02, .width = (uint32_t)width, .height = (uint32_t)height }; | ||||
|     euler::syscall::write_to_stream(socket, sizeof(dimensions_pkt), &dimensions_pkt); | ||||
| 
 | ||||
|     assert(title.size() <= UINT32_MAX); | ||||
| 
 | ||||
|     struct [[gnu::packed]] { uint8_t type; uint32_t length; } | ||||
|       title_pkt = {.type = 0x03, .length = (uint32_t)title.size() }; | ||||
|     euler::syscall::write_to_stream(socket, sizeof(title_pkt), &title_pkt); | ||||
|     euler::syscall::write_to_stream(socket, title.size(), title.data()); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   window::~window() { | ||||
|     euler::syscall::close_stream(socket); | ||||
|   } | ||||
| 
 | ||||
|   void window::show() { | ||||
|     uint8_t packet = 0; | ||||
|     euler::syscall::write_to_stream(socket, 1, &packet); | ||||
|   } | ||||
| 
 | ||||
|   void window::hide() { | ||||
|     uint8_t packet = 1; | ||||
|     euler::syscall::write_to_stream(socket, 1, &packet); | ||||
|   } | ||||
| 
 | ||||
|   void window::set_root(std::unique_ptr<widget> &&w) { | ||||
|     root = std::move(w); | ||||
|     root->notify_size(width, height); | ||||
|     root->render(contents, 0, 0, true); | ||||
|   } | ||||
| 
 | ||||
|   void window::render_and_send_to_compositor() { | ||||
| 
 | ||||
|     root->render(contents, 0, 0, false); | ||||
|     auto dirties = contents.get_dirty_regions(); | ||||
| 
 | ||||
|     for (auto it = dirties.cbegin(); it != dirties.cend(); ++it) { | ||||
| 
 | ||||
|       struct [[gnu::packed]] { | ||||
|         uint8_t type; | ||||
|         uint32_t start_x; uint32_t start_y; | ||||
|         uint32_t   width; uint32_t  height; | ||||
|       } update_pkt = { | ||||
|         .type = 0x04, | ||||
|         .start_x = (uint32_t)it->start_x, .start_y = (uint32_t)it->start_y, | ||||
|         .  width = (uint32_t)it->  width, . height = (uint32_t)it-> height | ||||
|       }; | ||||
| 
 | ||||
|       euler::syscall::write_to_stream(socket, sizeof(update_pkt), &update_pkt); | ||||
| 
 | ||||
|       for (int y = it->start_y; y < it->start_y + it->height; ++y) | ||||
|         euler::syscall::write_to_stream(socket, | ||||
|           it->width * sizeof(daguerre::hilbert_color), | ||||
|           &contents.image.at(it->start_x, y)); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     contents.clear_dirty(); | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										13
									
								
								makefile
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								makefile
									
										
									
									
									
								
							|  | @ -8,7 +8,7 @@ HILBERT_NASM = nasm -f elf64 | |||
| HILBERT_CC = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-c++ -std=c++20 \
 | ||||
|   ${EXTRA_CC_ARGS} -static -mno-sse -I include -I $(abspath euler/include) \
 | ||||
|   -I $(abspath libraries/daguerre/include) -I ${MINTSUKI_HEADERS_DIR} \
 | ||||
|   -I $(abspath libraries/goldman/include) | ||||
|   -I $(abspath libraries/pake/include) | ||||
| HILBERT_AR = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-ar | ||||
| HILBERT_LD = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-ld -z noexecstack | ||||
| 
 | ||||
|  | @ -25,6 +25,7 @@ LIBSTDCPP_DEP = toolchain/.libstdcpp-done | |||
| 
 | ||||
| EULER_DEP = toolchain/.euler-done | ||||
| DAGUERRE_DEP = toolchain/.daguerre-done | ||||
| PAKE_DEP = toolchain/.pake-done | ||||
| 
 | ||||
| APP_DEPS = ${EULER_DEP} | ||||
| LIBRARY_DEPS = ${LIBSTDCPP_DEP} | ||||
|  | @ -37,13 +38,14 @@ run: build/disk.iso | |||
| 	gdb -x qemu.gdb | ||||
| 
 | ||||
| clean: | ||||
| 	rm -rf build ${EULER_DEP} ${DAGUERRE_DEP} | ||||
| 	rm -rf build ${EULER_DEP} ${DAGUERRE_DEP} ${PAKE_DEP} | ||||
| 	make -C euler clean | ||||
| 	make -C kernel clean | ||||
| 	make -C applications/init clean | ||||
| 	make -C applications/goldman clean | ||||
| 	make -C applications/hello clean | ||||
| 	make -C libraries/daguerre clean | ||||
| 	make -C libraries/pake clean | ||||
| 
 | ||||
| clean-dependencies: clean | ||||
| 	rm -rf toolchain dependencies | ||||
|  | @ -110,6 +112,11 @@ ${DAGUERRE_DEP}: ${LIBRARY_DEPS} | |||
| 	cp libraries/daguerre/build/libdaguerre.a ${LIB_DIR}/ | ||||
| 	touch $@ | ||||
| 
 | ||||
| ${PAKE_DEP}: ${LIBRARY_DEPS} | ||||
| 	+make -C libraries/pake build/libpake.a | ||||
| 	cp libraries/pake/build/libpake.a ${LIB_DIR}/ | ||||
| 	touch $@ | ||||
| 
 | ||||
| kernel/build/kernel.elf: ${GCC_DEP} ${MINTSUKI_HEADERS_DEP} ${LIMINE_DEP} | ||||
| 	+make -C kernel build/kernel.elf | ||||
| 
 | ||||
|  | @ -119,7 +126,7 @@ applications/init/build/init.elf: ${APP_DEPS} | |||
| applications/goldman/build/goldman.elf: ${APP_DEPS} ${DAGUERRE_DEP} | ||||
| 	+make -C applications/goldman build/goldman.elf | ||||
| 
 | ||||
| applications/hello/build/hello.elf: ${APP_DEPS} ${DAGUERRE_DEP} | ||||
| applications/hello/build/hello.elf: ${APP_DEPS} ${DAGUERRE_DEP} ${PAKE_DEP} | ||||
| 	+make -C applications/hello build/hello.elf | ||||
| 
 | ||||
| build/initfs.tgz: applications/init/build/init.elf \ | ||||
|  |  | |||
		Reference in a new issue