diff options
author | Andreas Kling <kling@serenityos.org> | 2021-01-12 12:23:01 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-12 12:23:01 +0100 |
commit | c7ac7e6eaff862c1ea4f99e0c6ce75e095106d9c (patch) | |
tree | ce2a3fef96f0b8eebe907743f1e03739457c11a6 /Services | |
parent | 4055b0329117c1a280080bbd638eb48bafe29638 (diff) | |
download | serenity-c7ac7e6eaff862c1ea4f99e0c6ce75e095106d9c.zip |
Services: Move to Userland/Services/
Diffstat (limited to 'Services')
167 files changed, 0 insertions, 19750 deletions
diff --git a/Services/AudioServer/AudioClient.ipc b/Services/AudioServer/AudioClient.ipc deleted file mode 100644 index b43db52dd0..0000000000 --- a/Services/AudioServer/AudioClient.ipc +++ /dev/null @@ -1,6 +0,0 @@ -endpoint AudioClient = 82 -{ - FinishedPlayingBuffer(i32 buffer_id) =| - MutedStateChanged(bool muted) =| - MainMixVolumeChanged(i32 volume) =| -} diff --git a/Services/AudioServer/AudioServer.ipc b/Services/AudioServer/AudioServer.ipc deleted file mode 100644 index b48dcd7e46..0000000000 --- a/Services/AudioServer/AudioServer.ipc +++ /dev/null @@ -1,21 +0,0 @@ -endpoint AudioServer = 85 -{ - // Basic protocol - Greet() => (i32 client_id) - - // Mixer functions - SetMuted(bool muted) => () - GetMuted() => (bool muted) - GetMainMixVolume() => (i32 volume) - SetMainMixVolume(i32 volume) => () - - // Buffer playback - EnqueueBuffer(i32 buffer_id, int sample_count) => (bool success) - SetPaused(bool paused) => () - ClearBuffer(bool paused) => () - - //Buffer information - GetRemainingSamples() => (int remaining_samples) - GetPlayedSamples() => (int played_samples) - GetPlayingBuffer() => (i32 buffer_id) -} diff --git a/Services/AudioServer/CMakeLists.txt b/Services/AudioServer/CMakeLists.txt deleted file mode 100644 index 9c71197b62..0000000000 --- a/Services/AudioServer/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -compile_ipc(AudioServer.ipc AudioServerEndpoint.h) -compile_ipc(AudioClient.ipc AudioClientEndpoint.h) - -set(SOURCES - ClientConnection.cpp - Mixer.cpp - main.cpp - AudioServerEndpoint.h - AudioClientEndpoint.h -) - -serenity_bin(AudioServer) -target_link_libraries(AudioServer LibCore LibThread LibIPC) diff --git a/Services/AudioServer/ClientConnection.cpp b/Services/AudioServer/ClientConnection.cpp deleted file mode 100644 index c034dc5199..0000000000 --- a/Services/AudioServer/ClientConnection.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ClientConnection.h" -#include "Mixer.h" -#include <AK/SharedBuffer.h> -#include <AudioServer/AudioClientEndpoint.h> -#include <LibAudio/Buffer.h> -#include <errno.h> -#include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> - -namespace AudioServer { - -static HashMap<int, RefPtr<ClientConnection>> s_connections; - -void ClientConnection::for_each(Function<void(ClientConnection&)> callback) -{ - NonnullRefPtrVector<ClientConnection> connections; - for (auto& it : s_connections) - connections.append(*it.value); - for (auto& connection : connections) - callback(connection); -} - -ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id, Mixer& mixer) - : IPC::ClientConnection<AudioClientEndpoint, AudioServerEndpoint>(*this, move(client_socket), client_id) - , m_mixer(mixer) -{ - s_connections.set(client_id, *this); -} - -ClientConnection::~ClientConnection() -{ -} - -void ClientConnection::die() -{ - s_connections.remove(client_id()); -} - -void ClientConnection::did_finish_playing_buffer(Badge<BufferQueue>, int buffer_id) -{ - post_message(Messages::AudioClient::FinishedPlayingBuffer(buffer_id)); -} - -void ClientConnection::did_change_muted_state(Badge<Mixer>, bool muted) -{ - post_message(Messages::AudioClient::MutedStateChanged(muted)); -} - -void ClientConnection::did_change_main_mix_volume(Badge<Mixer>, int volume) -{ - post_message(Messages::AudioClient::MainMixVolumeChanged(volume)); -} - -OwnPtr<Messages::AudioServer::GreetResponse> ClientConnection::handle(const Messages::AudioServer::Greet&) -{ - return make<Messages::AudioServer::GreetResponse>(client_id()); -} - -OwnPtr<Messages::AudioServer::GetMainMixVolumeResponse> ClientConnection::handle(const Messages::AudioServer::GetMainMixVolume&) -{ - return make<Messages::AudioServer::GetMainMixVolumeResponse>(m_mixer.main_volume()); -} - -OwnPtr<Messages::AudioServer::SetMainMixVolumeResponse> ClientConnection::handle(const Messages::AudioServer::SetMainMixVolume& message) -{ - m_mixer.set_main_volume(message.volume()); - return make<Messages::AudioServer::SetMainMixVolumeResponse>(); -} - -OwnPtr<Messages::AudioServer::EnqueueBufferResponse> ClientConnection::handle(const Messages::AudioServer::EnqueueBuffer& message) -{ - auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.buffer_id()); - if (!shared_buffer) { - // FIXME: The shared buffer should have been retrieved for us already. - // We don't want to do IPC error checking at this layer. - ASSERT_NOT_REACHED(); - } - - if (!m_queue) - m_queue = m_mixer.create_queue(*this); - - if (m_queue->is_full()) - return make<Messages::AudioServer::EnqueueBufferResponse>(false); - - m_queue->enqueue(Audio::Buffer::create_with_shared_buffer(*shared_buffer, message.sample_count())); - return make<Messages::AudioServer::EnqueueBufferResponse>(true); -} - -OwnPtr<Messages::AudioServer::GetRemainingSamplesResponse> ClientConnection::handle(const Messages::AudioServer::GetRemainingSamples&) -{ - int remaining = 0; - if (m_queue) - remaining = m_queue->get_remaining_samples(); - - return make<Messages::AudioServer::GetRemainingSamplesResponse>(remaining); -} - -OwnPtr<Messages::AudioServer::GetPlayedSamplesResponse> ClientConnection::handle(const Messages::AudioServer::GetPlayedSamples&) -{ - int played = 0; - if (m_queue) - played = m_queue->get_played_samples(); - - return make<Messages::AudioServer::GetPlayedSamplesResponse>(played); -} - -OwnPtr<Messages::AudioServer::SetPausedResponse> ClientConnection::handle(const Messages::AudioServer::SetPaused& message) -{ - if (m_queue) - m_queue->set_paused(message.paused()); - return make<Messages::AudioServer::SetPausedResponse>(); -} - -OwnPtr<Messages::AudioServer::ClearBufferResponse> ClientConnection::handle(const Messages::AudioServer::ClearBuffer& message) -{ - if (m_queue) - m_queue->clear(message.paused()); - return make<Messages::AudioServer::ClearBufferResponse>(); -} - -OwnPtr<Messages::AudioServer::GetPlayingBufferResponse> ClientConnection::handle(const Messages::AudioServer::GetPlayingBuffer&) -{ - int id = -1; - if (m_queue) - id = m_queue->get_playing_buffer(); - return make<Messages::AudioServer::GetPlayingBufferResponse>(id); -} - -OwnPtr<Messages::AudioServer::GetMutedResponse> ClientConnection::handle(const Messages::AudioServer::GetMuted&) -{ - return make<Messages::AudioServer::GetMutedResponse>(m_mixer.is_muted()); -} - -OwnPtr<Messages::AudioServer::SetMutedResponse> ClientConnection::handle(const Messages::AudioServer::SetMuted& message) -{ - m_mixer.set_muted(message.muted()); - return make<Messages::AudioServer::SetMutedResponse>(); -} -} diff --git a/Services/AudioServer/ClientConnection.h b/Services/AudioServer/ClientConnection.h deleted file mode 100644 index f08aab49d3..0000000000 --- a/Services/AudioServer/ClientConnection.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/HashMap.h> -#include <AudioServer/AudioClientEndpoint.h> -#include <AudioServer/AudioServerEndpoint.h> -#include <LibIPC/ClientConnection.h> - -namespace Audio { -class Buffer; -} - -namespace AudioServer { - -class BufferQueue; -class Mixer; - -class ClientConnection final : public IPC::ClientConnection<AudioClientEndpoint, AudioServerEndpoint> - , public AudioServerEndpoint { - C_OBJECT(ClientConnection) -public: - explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id, Mixer& mixer); - ~ClientConnection() override; - - void did_finish_playing_buffer(Badge<BufferQueue>, int buffer_id); - void did_change_muted_state(Badge<Mixer>, bool muted); - void did_change_main_mix_volume(Badge<Mixer>, int volume); - - virtual void die() override; - - static void for_each(Function<void(ClientConnection&)>); - -private: - virtual OwnPtr<Messages::AudioServer::GreetResponse> handle(const Messages::AudioServer::Greet&) override; - virtual OwnPtr<Messages::AudioServer::GetMainMixVolumeResponse> handle(const Messages::AudioServer::GetMainMixVolume&) override; - virtual OwnPtr<Messages::AudioServer::SetMainMixVolumeResponse> handle(const Messages::AudioServer::SetMainMixVolume&) override; - virtual OwnPtr<Messages::AudioServer::EnqueueBufferResponse> handle(const Messages::AudioServer::EnqueueBuffer&) override; - virtual OwnPtr<Messages::AudioServer::GetRemainingSamplesResponse> handle(const Messages::AudioServer::GetRemainingSamples&) override; - virtual OwnPtr<Messages::AudioServer::GetPlayedSamplesResponse> handle(const Messages::AudioServer::GetPlayedSamples&) override; - virtual OwnPtr<Messages::AudioServer::SetPausedResponse> handle(const Messages::AudioServer::SetPaused&) override; - virtual OwnPtr<Messages::AudioServer::ClearBufferResponse> handle(const Messages::AudioServer::ClearBuffer&) override; - virtual OwnPtr<Messages::AudioServer::GetPlayingBufferResponse> handle(const Messages::AudioServer::GetPlayingBuffer&) override; - virtual OwnPtr<Messages::AudioServer::GetMutedResponse> handle(const Messages::AudioServer::GetMuted&) override; - virtual OwnPtr<Messages::AudioServer::SetMutedResponse> handle(const Messages::AudioServer::SetMuted&) override; - - Mixer& m_mixer; - RefPtr<BufferQueue> m_queue; -}; - -} diff --git a/Services/AudioServer/Mixer.cpp b/Services/AudioServer/Mixer.cpp deleted file mode 100644 index 66a33ed23d..0000000000 --- a/Services/AudioServer/Mixer.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Array.h> -#include <AK/MemoryStream.h> -#include <AK/NumericLimits.h> -#include <AudioServer/ClientConnection.h> -#include <AudioServer/Mixer.h> -#include <pthread.h> -#include <strings.h> - -namespace AudioServer { - -Mixer::Mixer() - : m_device(Core::File::construct("/dev/audio", this)) - , m_sound_thread(LibThread::Thread::construct( - [this] { - mix(); - return 0; - }, - "AudioServer[mixer]")) -{ - if (!m_device->open(Core::IODevice::WriteOnly)) { - dbgln("Can't open audio device: {}", m_device->error_string()); - return; - } - - pthread_mutex_init(&m_pending_mutex, nullptr); - pthread_cond_init(&m_pending_cond, nullptr); - - m_zero_filled_buffer = (u8*)malloc(4096); - bzero(m_zero_filled_buffer, 4096); - m_sound_thread->start(); -} - -Mixer::~Mixer() -{ -} - -NonnullRefPtr<BufferQueue> Mixer::create_queue(ClientConnection& client) -{ - auto queue = adopt(*new BufferQueue(client)); - pthread_mutex_lock(&m_pending_mutex); - m_pending_mixing.append(*queue); - m_added_queue = true; - pthread_cond_signal(&m_pending_cond); - pthread_mutex_unlock(&m_pending_mutex); - return queue; -} - -void Mixer::mix() -{ - decltype(m_pending_mixing) active_mix_queues; - - for (;;) { - if (active_mix_queues.is_empty() || m_added_queue) { - pthread_mutex_lock(&m_pending_mutex); - pthread_cond_wait(&m_pending_cond, &m_pending_mutex); - active_mix_queues.append(move(m_pending_mixing)); - pthread_mutex_unlock(&m_pending_mutex); - m_added_queue = false; - } - - active_mix_queues.remove_all_matching([&](auto& entry) { return !entry->client(); }); - - Audio::Sample mixed_buffer[1024]; - auto mixed_buffer_length = (int)(sizeof(mixed_buffer) / sizeof(Audio::Sample)); - - // Mix the buffers together into the output - for (auto& queue : active_mix_queues) { - if (!queue->client()) { - queue->clear(); - continue; - } - - for (int i = 0; i < mixed_buffer_length; ++i) { - auto& mixed_sample = mixed_buffer[i]; - Audio::Sample sample; - if (!queue->get_next_sample(sample)) - break; - mixed_sample += sample; - } - } - - if (m_muted) { - m_device->write(m_zero_filled_buffer, 4096); - } else { - Array<u8, 4096> buffer; - OutputMemoryStream stream { buffer }; - - for (int i = 0; i < mixed_buffer_length; ++i) { - auto& mixed_sample = mixed_buffer[i]; - - mixed_sample.scale(m_main_volume); - mixed_sample.clip(); - - LittleEndian<i16> out_sample; - out_sample = mixed_sample.left * NumericLimits<i16>::max(); - stream << out_sample; - - out_sample = mixed_sample.right * NumericLimits<i16>::max(); - stream << out_sample; - } - - ASSERT(stream.is_end()); - ASSERT(!stream.has_any_error()); - m_device->write(stream.data(), stream.size()); - } - } -} - -void Mixer::set_main_volume(int volume) -{ - if (volume > 100) - m_main_volume = 100; - else - m_main_volume = volume; - ClientConnection::for_each([volume](ClientConnection& client) { - client.did_change_main_mix_volume({}, volume); - }); -} - -void Mixer::set_muted(bool muted) -{ - if (m_muted == muted) - return; - m_muted = muted; - ClientConnection::for_each([muted](ClientConnection& client) { - client.did_change_muted_state({}, muted); - }); -} - -BufferQueue::BufferQueue(ClientConnection& client) - : m_client(client) -{ -} - -void BufferQueue::enqueue(NonnullRefPtr<Audio::Buffer>&& buffer) -{ - m_remaining_samples += buffer->sample_count(); - m_queue.enqueue(move(buffer)); -} -} diff --git a/Services/AudioServer/Mixer.h b/Services/AudioServer/Mixer.h deleted file mode 100644 index 74fbba6ede..0000000000 --- a/Services/AudioServer/Mixer.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "ClientConnection.h" -#include <AK/Atomic.h> -#include <AK/Badge.h> -#include <AK/ByteBuffer.h> -#include <AK/NonnullRefPtrVector.h> -#include <AK/Queue.h> -#include <AK/RefCounted.h> -#include <AK/WeakPtr.h> -#include <LibAudio/Buffer.h> -#include <LibCore/File.h> -#include <LibThread/Lock.h> -#include <LibThread/Thread.h> - -namespace AudioServer { - -class ClientConnection; - -class BufferQueue : public RefCounted<BufferQueue> { -public: - explicit BufferQueue(ClientConnection&); - ~BufferQueue() { } - - bool is_full() const { return m_queue.size() >= 3; } - void enqueue(NonnullRefPtr<Audio::Buffer>&&); - - bool get_next_sample(Audio::Sample& sample) - { - if (m_paused) - return false; - - while (!m_current && !m_queue.is_empty()) - m_current = m_queue.dequeue(); - - if (!m_current) - return false; - - sample = m_current->samples()[m_position++]; - --m_remaining_samples; - ++m_played_samples; - - if (m_position >= m_current->sample_count()) { - m_client->did_finish_playing_buffer({}, m_current->shbuf_id()); - m_current = nullptr; - m_position = 0; - } - return true; - } - - ClientConnection* client() { return m_client.ptr(); } - - void clear(bool paused = false) - { - m_queue.clear(); - m_position = 0; - m_remaining_samples = 0; - m_played_samples = 0; - m_current = nullptr; - m_paused = paused; - } - - void set_paused(bool paused) - { - m_paused = paused; - } - - int get_remaining_samples() const { return m_remaining_samples; } - int get_played_samples() const { return m_played_samples; } - int get_playing_buffer() const - { - if (m_current) - return m_current->shbuf_id(); - return -1; - } - -private: - RefPtr<Audio::Buffer> m_current; - Queue<NonnullRefPtr<Audio::Buffer>> m_queue; - int m_position { 0 }; - int m_remaining_samples { 0 }; - int m_played_samples { 0 }; - bool m_paused { false }; - WeakPtr<ClientConnection> m_client; -}; - -class Mixer : public Core::Object { - C_OBJECT(Mixer) -public: - Mixer(); - virtual ~Mixer() override; - - NonnullRefPtr<BufferQueue> create_queue(ClientConnection&); - - int main_volume() const { return m_main_volume; } - void set_main_volume(int volume); - - bool is_muted() const { return m_muted; } - void set_muted(bool); - -private: - Vector<NonnullRefPtr<BufferQueue>> m_pending_mixing; - Atomic<bool> m_added_queue { false }; - pthread_mutex_t m_pending_mutex; - pthread_cond_t m_pending_cond; - - RefPtr<Core::File> m_device; - - NonnullRefPtr<LibThread::Thread> m_sound_thread; - - bool m_muted { false }; - int m_main_volume { 100 }; - - u8* m_zero_filled_buffer { nullptr }; - - void mix(); -}; -} diff --git a/Services/AudioServer/main.cpp b/Services/AudioServer/main.cpp deleted file mode 100644 index 9eca20bfd3..0000000000 --- a/Services/AudioServer/main.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Mixer.h" -#include <LibCore/File.h> -#include <LibCore/LocalServer.h> - -int main(int, char**) -{ - if (pledge("stdio thread shared_buffer accept rpath wpath cpath unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - Core::EventLoop event_loop; - AudioServer::Mixer mixer; - - auto server = Core::LocalServer::construct(); - bool ok = server->take_over_from_system_server(); - ASSERT(ok); - server->on_ready_to_accept = [&] { - auto client_socket = server->accept(); - if (!client_socket) { - dbgln("AudioServer: accept failed."); - return; - } - static int s_next_client_id = 0; - int client_id = ++s_next_client_id; - IPC::new_client_connection<AudioServer::ClientConnection>(client_socket.release_nonnull(), client_id, mixer); - }; - - if (pledge("stdio thread shared_buffer accept", nullptr) < 0) { - perror("pledge"); - return 1; - } - - unveil(nullptr, nullptr); - - return event_loop.exec(); -} diff --git a/Services/CMakeLists.txt b/Services/CMakeLists.txt deleted file mode 100644 index 26003a7d55..0000000000 --- a/Services/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -add_subdirectory(AudioServer) -add_subdirectory(ChessEngine) -add_subdirectory(Clipboard) -add_subdirectory(CrashDaemon) -add_subdirectory(DHCPClient) -add_subdirectory(EchoServer) -add_subdirectory(ImageDecoder) -add_subdirectory(LaunchServer) -add_subdirectory(LookupServer) -add_subdirectory(NotificationServer) -add_subdirectory(ProtocolServer) -add_subdirectory(SystemMenu) -add_subdirectory(SystemServer) -add_subdirectory(Taskbar) -add_subdirectory(TelnetServer) -add_subdirectory(WebContent) -add_subdirectory(WebServer) -add_subdirectory(WindowServer) diff --git a/Services/ChessEngine/CMakeLists.txt b/Services/ChessEngine/CMakeLists.txt deleted file mode 100644 index 288529a100..0000000000 --- a/Services/ChessEngine/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - ChessEngine.cpp - main.cpp - MCTSTree.cpp -) - -serenity_bin(ChessEngine) -target_link_libraries(ChessEngine LibChess LibCore) diff --git a/Services/ChessEngine/ChessEngine.cpp b/Services/ChessEngine/ChessEngine.cpp deleted file mode 100644 index 16394d443c..0000000000 --- a/Services/ChessEngine/ChessEngine.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ChessEngine.h" -#include "MCTSTree.h" -#include <LibCore/ElapsedTimer.h> - -using namespace Chess::UCI; - -void ChessEngine::handle_uci() -{ - send_command(IdCommand(IdCommand::Type::Name, "ChessEngine")); - send_command(IdCommand(IdCommand::Type::Author, "the SerenityOS developers")); - send_command(UCIOkCommand()); -} - -void ChessEngine::handle_position(const PositionCommand& command) -{ - // FIXME: Implement fen board position. - ASSERT(!command.fen().has_value()); - m_board = Chess::Board(); - for (auto& move : command.moves()) { - ASSERT(m_board.apply_move(move)); - } -} - -void ChessEngine::handle_go(const GoCommand& command) -{ - // FIXME: A better algorithm than naive mcts. - // FIXME: Add different ways to terminate search. - ASSERT(command.movetime.has_value()); - - srand(arc4random()); - - Core::ElapsedTimer elapsed_time; - elapsed_time.start(); - - MCTSTree mcts(m_board); - - // FIXME: optimize simulations enough for use. - mcts.set_eval_method(MCTSTree::EvalMethod::Heuristic); - - int rounds = 0; - while (elapsed_time.elapsed() <= command.movetime.value()) { - mcts.do_round(); - ++rounds; - } - dbg() << "MCTS finished " << rounds << " rounds."; - dbg() << "MCTS evaluation " << mcts.expected_value(); - auto best_move = mcts.best_move(); - dbg() << "MCTS best move " << best_move.to_long_algebraic(); - send_command(BestMoveCommand(best_move)); -} diff --git a/Services/ChessEngine/ChessEngine.h b/Services/ChessEngine/ChessEngine.h deleted file mode 100644 index 54c0cb7c8a..0000000000 --- a/Services/ChessEngine/ChessEngine.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <LibChess/Chess.h> -#include <LibChess/UCIEndpoint.h> - -class ChessEngine : public Chess::UCI::Endpoint { - C_OBJECT(ChessEngine) -public: - virtual ~ChessEngine() override { } - - ChessEngine() { } - ChessEngine(NonnullRefPtr<Core::IODevice> in, NonnullRefPtr<Core::IODevice> out) - : Endpoint(in, out) - { - } - - virtual void handle_uci(); - virtual void handle_position(const Chess::UCI::PositionCommand&); - virtual void handle_go(const Chess::UCI::GoCommand&); - -private: - Chess::Board m_board; -}; diff --git a/Services/ChessEngine/MCTSTree.cpp b/Services/ChessEngine/MCTSTree.cpp deleted file mode 100644 index dc195089fe..0000000000 --- a/Services/ChessEngine/MCTSTree.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "MCTSTree.h" -#include <AK/String.h> -#include <stdlib.h> - -MCTSTree::MCTSTree(const Chess::Board& board, double exploration_parameter, MCTSTree* parent) - : m_parent(parent) - , m_exploration_parameter(exploration_parameter) - , m_board(board) -{ - if (m_parent) - m_eval_method = m_parent->eval_method(); -} - -MCTSTree& MCTSTree::select_leaf() -{ - if (!expanded() || m_children.size() == 0) - return *this; - - MCTSTree* node = nullptr; - double max_uct = -double(INFINITY); - for (auto& child : m_children) { - double uct = child.uct(m_board.turn()); - if (uct >= max_uct) { - max_uct = uct; - node = &child; - } - } - ASSERT(node); - return node->select_leaf(); -} - -MCTSTree& MCTSTree::expand() -{ - ASSERT(!expanded() || m_children.size() == 0); - - if (!m_moves_generated) { - m_board.generate_moves([&](Chess::Move move) { - Chess::Board clone = m_board; - clone.apply_move(move); - m_children.append(make<MCTSTree>(clone, m_exploration_parameter, this)); - return IterationDecision::Continue; - }); - m_moves_generated = true; - } - - if (m_children.size() == 0) { - return *this; - } - - for (auto& child : m_children) { - if (child.m_simulations == 0) { - return child; - } - } - ASSERT_NOT_REACHED(); -} - -int MCTSTree::simulate_game() const -{ - ASSERT_NOT_REACHED(); - Chess::Board clone = m_board; - while (!clone.game_finished()) { - clone.apply_move(clone.random_move()); - } - return clone.game_score(); -} - -int MCTSTree::heuristic() const -{ - if (m_board.game_finished()) - return m_board.game_score(); - - double winchance = max(min(double(m_board.material_imbalance()) / 6, 1.0), -1.0); - - double random = double(rand()) / RAND_MAX; - if (winchance >= random) - return 1; - if (winchance <= -random) - return -1; - - return 0; -} - -void MCTSTree::apply_result(int game_score) -{ - m_simulations++; - m_white_points += game_score; - - if (m_parent) - m_parent->apply_result(game_score); -} - -void MCTSTree::do_round() -{ - auto& node = select_leaf().expand(); - - int result; - if (m_eval_method == EvalMethod::Simulation) { - result = node.simulate_game(); - } else { - result = node.heuristic(); - } - node.apply_result(result); -} - -Chess::Move MCTSTree::best_move() const -{ - int score_multiplier = (m_board.turn() == Chess::Color::White) ? 1 : -1; - - Chess::Move best_move = { { 0, 0 }, { 0, 0 } }; - double best_score = -double(INFINITY); - ASSERT(m_children.size()); - for (auto& node : m_children) { - double node_score = node.expected_value() * score_multiplier; - if (node_score >= best_score) { - // The best move is the last move made in the child. - best_move = node.m_board.moves()[node.m_board.moves().size() - 1]; - best_score = node_score; - } - } - - return best_move; -} - -double MCTSTree::expected_value() const -{ - if (m_simulations == 0) - return 0; - - return double(m_white_points) / m_simulations; -} - -double MCTSTree::uct(Chess::Color color) const -{ - // UCT: Upper Confidence Bound Applied to Trees. - // Kocsis, Levente; Szepesvári, Csaba (2006). "Bandit based Monte-Carlo Planning" - - // Fun fact: Szepesvári was my data structures professor. - double expected = expected_value() * ((color == Chess::Color::White) ? 1 : -1); - return expected + m_exploration_parameter * sqrt(log(m_parent->m_simulations) / m_simulations); -} - -bool MCTSTree::expanded() const -{ - if (!m_moves_generated) - return false; - - for (auto& child : m_children) { - if (child.m_simulations == 0) - return false; - } - - return true; -} diff --git a/Services/ChessEngine/MCTSTree.h b/Services/ChessEngine/MCTSTree.h deleted file mode 100644 index 0ae68eae8f..0000000000 --- a/Services/ChessEngine/MCTSTree.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Function.h> -#include <AK/NonnullOwnPtrVector.h> -#include <LibChess/Chess.h> -#include <math.h> - -class MCTSTree { -public: - enum EvalMethod { - Simulation, - Heuristic, - }; - - MCTSTree(const Chess::Board& board, double exploration_parameter = sqrt(2), MCTSTree* parent = nullptr); - - MCTSTree& select_leaf(); - MCTSTree& expand(); - int simulate_game() const; - int heuristic() const; - void apply_result(int game_score); - void do_round(); - - Chess::Move best_move() const; - double expected_value() const; - double uct(Chess::Color color) const; - bool expanded() const; - - EvalMethod eval_method() const { return m_eval_method; } - void set_eval_method(EvalMethod method) { m_eval_method = method; } - -private: - NonnullOwnPtrVector<MCTSTree> m_children; - MCTSTree* m_parent { nullptr }; - int m_white_points { 0 }; - int m_simulations { 0 }; - bool m_moves_generated { false }; - double m_exploration_parameter; - EvalMethod m_eval_method { EvalMethod::Simulation }; - Chess::Board m_board; -}; diff --git a/Services/ChessEngine/main.cpp b/Services/ChessEngine/main.cpp deleted file mode 100644 index 60d6ec8397..0000000000 --- a/Services/ChessEngine/main.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ChessEngine.h" -#include <LibCore/EventLoop.h> -#include <LibCore/File.h> - -int main() -{ - if (pledge("stdio shared_buffer accept unix rpath cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - Core::EventLoop loop; - if (pledge("stdio shared_buffer unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - auto engine = ChessEngine::construct(Core::File::standard_input(), Core::File::standard_output()); - return loop.exec(); -} diff --git a/Services/Clipboard/CMakeLists.txt b/Services/Clipboard/CMakeLists.txt deleted file mode 100644 index 4f0e5ab65a..0000000000 --- a/Services/Clipboard/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -compile_ipc(ClipboardServer.ipc ClipboardServerEndpoint.h) -compile_ipc(ClipboardClient.ipc ClipboardClientEndpoint.h) - -set(SOURCES - ClientConnection.cpp - ClipboardClientEndpoint.h - ClipboardServerEndpoint.h - Storage.cpp - main.cpp -) - -serenity_bin(Clipboard) -target_link_libraries(Clipboard LibCore LibIPC) diff --git a/Services/Clipboard/ClientConnection.cpp b/Services/Clipboard/ClientConnection.cpp deleted file mode 100644 index 9518a4f0b6..0000000000 --- a/Services/Clipboard/ClientConnection.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Badge.h> -#include <AK/SharedBuffer.h> -#include <Clipboard/ClientConnection.h> -#include <Clipboard/ClipboardClientEndpoint.h> -#include <Clipboard/Storage.h> - -namespace Clipboard { - -static HashMap<int, RefPtr<ClientConnection>> s_connections; - -void ClientConnection::for_each_client(Function<void(ClientConnection&)> callback) -{ - for (auto& it : s_connections) { - callback(*it.value); - } -} - -ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id) - : IPC::ClientConnection<ClipboardClientEndpoint, ClipboardServerEndpoint>(*this, move(socket), client_id) -{ - s_connections.set(client_id, *this); -} - -ClientConnection::~ClientConnection() -{ -} - -void ClientConnection::die() -{ - s_connections.remove(client_id()); -} - -OwnPtr<Messages::ClipboardServer::GreetResponse> ClientConnection::handle(const Messages::ClipboardServer::Greet&) -{ - return make<Messages::ClipboardServer::GreetResponse>(client_id()); -} - -OwnPtr<Messages::ClipboardServer::SetClipboardDataResponse> ClientConnection::handle(const Messages::ClipboardServer::SetClipboardData& message) -{ - auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.shbuf_id()); - if (!shared_buffer) { - did_misbehave("SetClipboardData: Bad shared buffer ID"); - return {}; - } - Storage::the().set_data(*shared_buffer, message.data_size(), message.mime_type(), message.metadata().entries()); - return make<Messages::ClipboardServer::SetClipboardDataResponse>(); -} - -OwnPtr<Messages::ClipboardServer::GetClipboardDataResponse> ClientConnection::handle(const Messages::ClipboardServer::GetClipboardData&) -{ - auto& storage = Storage::the(); - - i32 shbuf_id = -1; - if (storage.data_size()) { - // FIXME: Optimize case where an app is copy/pasting within itself. - // We can just reuse the SharedBuffer then, since it will have the same peer PID. - // It would be even nicer if a SharedBuffer could have an arbitrary number of clients.. - RefPtr<SharedBuffer> shared_buffer = SharedBuffer::create_with_size(storage.data_size()); - ASSERT(shared_buffer); - memcpy(shared_buffer->data<void>(), storage.data(), storage.data_size()); - shared_buffer->seal(); - shared_buffer->share_with(client_pid()); - shbuf_id = shared_buffer->shbuf_id(); - - // FIXME: This is a workaround for the fact that SharedBuffers will go away if neither side is retaining them. - // After we respond to GetClipboardData, we have to wait for the client to ref the buffer on his side. - m_last_sent_buffer = move(shared_buffer); - } - return make<Messages::ClipboardServer::GetClipboardDataResponse>(shbuf_id, storage.data_size(), storage.mime_type(), storage.metadata()); -} - -void ClientConnection::notify_about_clipboard_change() -{ - post_message(Messages::ClipboardClient::ClipboardDataChanged(Storage::the().mime_type())); -} - -} diff --git a/Services/Clipboard/ClientConnection.h b/Services/Clipboard/ClientConnection.h deleted file mode 100644 index 48794f4f88..0000000000 --- a/Services/Clipboard/ClientConnection.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/HashMap.h> -#include <Clipboard/ClipboardClientEndpoint.h> -#include <Clipboard/ClipboardServerEndpoint.h> -#include <LibIPC/ClientConnection.h> - -namespace Clipboard { - -class ClientConnection final - : public IPC::ClientConnection<ClipboardClientEndpoint, ClipboardServerEndpoint> - , public ClipboardServerEndpoint { - - C_OBJECT(ClientConnection); - -public: - explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); - virtual ~ClientConnection() override; - - virtual void die() override; - - static void for_each_client(Function<void(ClientConnection&)>); - - void notify_about_clipboard_change(); - -private: - virtual OwnPtr<Messages::ClipboardServer::GreetResponse> handle(const Messages::ClipboardServer::Greet&) override; - virtual OwnPtr<Messages::ClipboardServer::GetClipboardDataResponse> handle(const Messages::ClipboardServer::GetClipboardData&) override; - virtual OwnPtr<Messages::ClipboardServer::SetClipboardDataResponse> handle(const Messages::ClipboardServer::SetClipboardData&) override; - - RefPtr<SharedBuffer> m_last_sent_buffer; -}; - -} diff --git a/Services/Clipboard/ClipboardClient.ipc b/Services/Clipboard/ClipboardClient.ipc deleted file mode 100644 index 50d695e92a..0000000000 --- a/Services/Clipboard/ClipboardClient.ipc +++ /dev/null @@ -1,4 +0,0 @@ -endpoint ClipboardClient = 804 -{ - ClipboardDataChanged([UTF8] String mime_type) =| -} diff --git a/Services/Clipboard/ClipboardServer.ipc b/Services/Clipboard/ClipboardServer.ipc deleted file mode 100644 index c0c1fab8c8..0000000000 --- a/Services/Clipboard/ClipboardServer.ipc +++ /dev/null @@ -1,7 +0,0 @@ -endpoint ClipboardServer = 802 -{ - Greet() => (i32 client_id) - - GetClipboardData() => (i32 shbuf_id, i32 data_size, [UTF8] String mime_type, IPC::Dictionary metadata) - SetClipboardData(i32 shbuf_id, i32 data_size, [UTF8] String mime_type, IPC::Dictionary metadata) => () -} diff --git a/Services/Clipboard/Storage.cpp b/Services/Clipboard/Storage.cpp deleted file mode 100644 index 3b745dea16..0000000000 --- a/Services/Clipboard/Storage.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <Clipboard/Storage.h> - -namespace Clipboard { - -Storage& Storage::the() -{ - static Storage* s_the; - if (!s_the) - s_the = new Storage; - return *s_the; -} - -Storage::Storage() -{ -} - -Storage::~Storage() -{ -} - -void Storage::set_data(NonnullRefPtr<SharedBuffer> data, size_t data_size, const String& mime_type, const HashMap<String, String>& metadata) -{ - dbg() << "Storage::set_data <- [" << mime_type << "] " << data->data<void>() << " (" << data_size << " bytes)"; - for (auto& it : metadata) { - dbg() << " " << it.key << ": " << it.value; - } - m_shared_buffer = move(data); - m_data_size = data_size; - m_mime_type = mime_type; - m_metadata = metadata; - - if (on_content_change) - on_content_change(); -} - -} diff --git a/Services/Clipboard/Storage.h b/Services/Clipboard/Storage.h deleted file mode 100644 index ce194c15f4..0000000000 --- a/Services/Clipboard/Storage.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Function.h> -#include <AK/HashMap.h> -#include <AK/SharedBuffer.h> -#include <AK/String.h> - -namespace Clipboard { - -class Storage { -public: - static Storage& the(); - ~Storage(); - - bool has_data() const { return m_shared_buffer; } - - const String& mime_type() const { return m_mime_type; } - const HashMap<String, String>& metadata() const { return m_metadata; } - - const u8* data() const - { - if (!has_data()) - return nullptr; - return m_shared_buffer->data<u8>(); - } - - size_t data_size() const - { - if (has_data()) - return m_data_size; - return 0; - } - - void set_data(NonnullRefPtr<SharedBuffer>, size_t data_size, const String& mime_type, const HashMap<String, String>& metadata); - - Function<void()> on_content_change; - -private: - Storage(); - - String m_mime_type; - RefPtr<SharedBuffer> m_shared_buffer; - size_t m_data_size { 0 }; - HashMap<String, String> m_metadata; -}; - -} diff --git a/Services/Clipboard/main.cpp b/Services/Clipboard/main.cpp deleted file mode 100644 index b6cae095ee..0000000000 --- a/Services/Clipboard/main.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <Clipboard/ClientConnection.h> -#include <Clipboard/Storage.h> -#include <LibCore/EventLoop.h> -#include <LibCore/LocalServer.h> -#include <LibIPC/ClientConnection.h> - -int main(int, char**) -{ - if (pledge("stdio shared_buffer accept unix rpath cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - Core::EventLoop event_loop; - if (pledge("stdio shared_buffer unix accept", nullptr) < 0) { - perror("pledge"); - return 1; - } - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - auto server = Core::LocalServer::construct(); - bool ok = server->take_over_from_system_server(); - ASSERT(ok); - - if (pledge("stdio shared_buffer accept", nullptr) < 0) { - perror("pledge"); - return 1; - } - - server->on_ready_to_accept = [&] { - auto client_socket = server->accept(); - if (!client_socket) { - dbgln("Clipboard: accept failed."); - return; - } - static int s_next_client_id = 0; - int client_id = ++s_next_client_id; - IPC::new_client_connection<Clipboard::ClientConnection>(client_socket.release_nonnull(), client_id); - }; - - Clipboard::Storage::the().on_content_change = [&] { - Clipboard::ClientConnection::for_each_client([&](auto& client) { - client.notify_about_clipboard_change(); - }); - }; - - return event_loop.exec(); -} diff --git a/Services/CrashDaemon/CMakeLists.txt b/Services/CrashDaemon/CMakeLists.txt deleted file mode 100644 index 5dcf13aacb..0000000000 --- a/Services/CrashDaemon/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(SOURCES - main.cpp -) - -serenity_bin(CrashDaemon) -target_link_libraries(CrashDaemon LibC LibCore LibCoreDump) diff --git a/Services/CrashDaemon/main.cpp b/Services/CrashDaemon/main.cpp deleted file mode 100644 index 2453f73699..0000000000 --- a/Services/CrashDaemon/main.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/LexicalPath.h> -#include <LibCore/DirectoryWatcher.h> -#include <LibCoreDump/Backtrace.h> -#include <LibCoreDump/Reader.h> -#include <serenity.h> -#include <spawn.h> -#include <sys/stat.h> -#include <time.h> -#include <unistd.h> - -static void wait_until_coredump_is_ready(const String& coredump_path) -{ - while (true) { - struct stat statbuf; - if (stat(coredump_path.characters(), &statbuf) < 0) { - perror("stat"); - ASSERT_NOT_REACHED(); - } - if (statbuf.st_mode & 0400) // Check if readable - break; - - usleep(10000); // sleep for 10ms - } -} - -static void print_backtrace(const String& coredump_path) -{ - auto coredump = CoreDump::Reader::create(coredump_path); - if (!coredump) { - dbgln("Could not open coredump '{}'", coredump_path); - return; - } - for (auto& entry : coredump->backtrace().entries()) - dbgln("{}", entry.to_string(true)); -} - -static void launch_crash_reporter(const String& coredump_path) -{ - pid_t child; - const char* argv[] = { "CrashReporter", coredump_path.characters(), nullptr, nullptr }; - if ((errno = posix_spawn(&child, "/bin/CrashReporter", nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - } else { - if (disown(child) < 0) - perror("disown"); - } -} - -int main() -{ - if (pledge("stdio rpath proc exec", nullptr) < 0) { - perror("pledge"); - return 1; - } - - Core::DirectoryWatcher watcher { "/tmp/coredump" }; - while (true) { - auto event = watcher.wait_for_event(); - ASSERT(event.has_value()); - if (event.value().type != Core::DirectoryWatcher::Event::Type::ChildAdded) - continue; - auto coredump_path = event.value().child_path; - dbgln("New coredump file: {}", coredump_path); - wait_until_coredump_is_ready(coredump_path); - print_backtrace(coredump_path); - launch_crash_reporter(coredump_path); - } -} diff --git a/Services/DHCPClient/CMakeLists.txt b/Services/DHCPClient/CMakeLists.txt deleted file mode 100644 index 6fddd4a21f..0000000000 --- a/Services/DHCPClient/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - DHCPv4Client.cpp - DHCPv4.cpp - main.cpp -) - -serenity_bin(DHCPClient) -target_link_libraries(DHCPClient LibCore) diff --git a/Services/DHCPClient/DHCPv4.cpp b/Services/DHCPClient/DHCPv4.cpp deleted file mode 100644 index 36430f4eec..0000000000 --- a/Services/DHCPClient/DHCPv4.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "DHCPv4.h" - -//#define DHCPV4_DEBUG - -ParsedDHCPv4Options DHCPv4Packet::parse_options() const -{ - ParsedDHCPv4Options options; - for (size_t index = 4; index < DHCPV4_OPTION_FIELD_MAX_LENGTH; ++index) { - auto opt_name = *(const DHCPOption*)&m_options[index]; - switch (opt_name) { - case DHCPOption::Pad: - continue; - case DHCPOption::End: - goto escape; - default: - ++index; - auto length = m_options[index]; - if ((size_t)length > DHCPV4_OPTION_FIELD_MAX_LENGTH - index) { - dbg() << "Bogus option length " << length << " assuming forgotten END"; - break; - } -#ifdef DHCPV4_DEBUG - dbg() << "DHCP Option " << (u8)opt_name << " with length " << length; -#endif - ++index; - options.options.set(opt_name, { length, &m_options[index] }); - index += length - 1; - break; - } - } -escape:; - return options; -} diff --git a/Services/DHCPClient/DHCPv4.h b/Services/DHCPClient/DHCPv4.h deleted file mode 100644 index a773167683..0000000000 --- a/Services/DHCPClient/DHCPv4.h +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Assertions.h> -#include <AK/ByteBuffer.h> -#include <AK/Endian.h> -#include <AK/HashMap.h> -#include <AK/IPv4Address.h> -#include <AK/MACAddress.h> -#include <AK/StringBuilder.h> -#include <AK/StringView.h> -#include <AK/Traits.h> -#include <AK/Types.h> -#include <string.h> - -enum class DHCPv4Flags : u16 { - Broadcast = 1, - /* everything else is reserved and must be zero */ -}; - -enum class DHCPv4Op : u8 { - BootRequest = 1, - BootReply = 2 -}; - -enum class DHCPOption : u8 { - // BOOTP - Pad = 0, - SubnetMask, - TimeOffset, - Router, - TimeServer, - NameServer, - DomainNameServer, - LogServer, - CookieServer, - LPRServer, - ImpressServer, - ResourceLocationServer, - HostName, - BootFileSize, - MeritDumpFile, - DomainName, - SwapServer, - RootPath, - ExtensionsPath, - IPForwardingEnableDisable, - NonLocalSourceRoutingEnableDisable, - PolicyFilter, - MaximumDatagramReassemblySize, - DefaultIPTTL, - PathMTUAgingTimeout, - PathMTUPlateauTable, - InterfaceMTU, - AllSubnetsAreLocal, - BroadcastAddress, - PerformMaskDiscovery, - MaskSupplier, - PerformRouterDiscovery, - RouterSolicitationAddress, - StaticRoute, - TrailerEncapsulation, - ARPCacheTimeout, - EthernetEncapsulation, - TCPDefaultTTL, - TCPKeepaliveInterval, - TCPKeepaliveGarbage, - NetworkInformationServiceDomain, - NetworkInformationServers, - NetworkTimeProtocolServers, - VendorSpecificInformation, - NetBIOSOverTCPIPNameServer, - NetBIOSOverTCPIPDatagramDistributionServer, - NetBIOSOverTCPIPNodeType, - NetBIOSOverTCPIPScope, - XWindowSystemFontServer, // wow - XWindowSystemDisplayManager, - // DHCP - RequestedIPAddress = 50, - IPAddressLeaseTime, - OptionOverload, - DHCPMessageType, - ServerIdentifier, - ParameterRequestList, - Message, - MaximumDHCPMessageSize, - RenewalT1Time, - RenewalT2Time, - ClassIdentifier, - ClientIdentifier, - End = 255 -}; - -enum class DHCPMessageType : u8 { - DHCPDiscover = 1, - DHCPOffer, - DHCPRequest, - DHCPDecline, - DHCPAck, - DHCPNak, - DHCPRelease, -}; - -template<> -struct AK::Traits<DHCPOption> : public AK::GenericTraits<DHCPOption> { - static constexpr bool is_trivial() { return true; } - static unsigned hash(DHCPOption u) { return int_hash((u8)u); } -}; - -struct ParsedDHCPv4Options { - template<typename T> - Optional<const T> get(DHCPOption option_name) const - { - auto option = options.get(option_name); - if (!option.has_value()) { - return {}; - } - auto& value = option.value(); - if (value.length != sizeof(T)) - return {}; - return *(const T*)value.value; - } - - template<typename T> - Vector<T> get_many(DHCPOption option_name, size_t max_number) const - { - Vector<T> values; - - auto option = options.get(option_name); - if (!option.has_value()) { - return {}; - } - auto& value = option.value(); - if (value.length < sizeof(T)) - return {}; - - for (size_t i = 0; i < max_number; ++i) { - auto offset = i * sizeof(T); - if (offset >= value.length) - break; - values.append(*(T*)((u8*)const_cast<void*>(value.value) + offset)); - } - - return values; - } - - String to_string() const - { - StringBuilder builder; - builder.append("DHCP Options ("); - builder.appendf("%zu", options.size()); - builder.append(" entries)\n"); - for (auto& opt : options) { - builder.appendf("\toption %d (%d bytes):", (u8)opt.key, (u8)opt.value.length); - for (auto i = 0; i < opt.value.length; ++i) - builder.appendf(" %u ", ((const u8*)opt.value.value)[i]); - builder.append('\n'); - } - return builder.build(); - } - - struct DHCPOptionValue { - u8 length; - const void* value; - }; - - HashMap<DHCPOption, DHCPOptionValue> options; -}; - -constexpr auto DHCPV4_OPTION_FIELD_MAX_LENGTH = 312; - -class [[gnu::packed]] DHCPv4Packet { -public: - u8 op() const { return m_op; } - void set_op(DHCPv4Op op) { m_op = (u8)op; } - - u8 htype() const { return m_htype; } - void set_htype(u8 htype) { m_htype = htype; } - - u8 hlen() const { return m_hlen; } - void set_hlen(u8 hlen) { m_hlen = hlen; } - - u8 hops() const { return m_hops; } - void set_hops(u8 hops) { m_hops = hops; } - - u32 xid() const { return m_xid; } - void set_xid(u32 xid) { m_xid = xid; } - - u16 secs() const { return m_secs; } - void set_secs(u16 secs) { m_secs = secs; } - - u16 flags() const { return m_flags; } - void set_flags(DHCPv4Flags flags) { m_flags = (u16)flags; } - - const IPv4Address& ciaddr() const { return m_ciaddr; } - const IPv4Address& yiaddr() const { return m_yiaddr; } - const IPv4Address& siaddr() const { return m_siaddr; } - const IPv4Address& giaddr() const { return m_giaddr; } - - IPv4Address& ciaddr() { return m_ciaddr; } - IPv4Address& yiaddr() { return m_yiaddr; } - IPv4Address& siaddr() { return m_siaddr; } - IPv4Address& giaddr() { return m_giaddr; } - - u8* options() { return m_options; } - ParsedDHCPv4Options parse_options() const; - - const MACAddress& chaddr() const { return *(const MACAddress*)&m_chaddr[0]; } - void set_chaddr(const MACAddress& mac) { *(MACAddress*)&m_chaddr[0] = mac; } - - StringView sname() const { return { (const char*)&m_sname[0] }; } - StringView file() const { return { (const char*)&m_file[0] }; } - -private: - NetworkOrdered<u8> m_op; - NetworkOrdered<u8> m_htype; - NetworkOrdered<u8> m_hlen; - NetworkOrdered<u8> m_hops; - NetworkOrdered<u32> m_xid; - NetworkOrdered<u16> m_secs; - NetworkOrdered<u16> m_flags; - IPv4Address m_ciaddr; - IPv4Address m_yiaddr; - IPv4Address m_siaddr; - IPv4Address m_giaddr; - u8 m_chaddr[16]; // 10 bytes of padding at the end - u8 m_sname[64] { 0 }; - u8 m_file[128] { 0 }; - u8 m_options[DHCPV4_OPTION_FIELD_MAX_LENGTH] { 0 }; // variable, less than 312 bytes -}; - -class DHCPv4PacketBuilder { -public: - DHCPv4PacketBuilder() - : m_buffer(ByteBuffer::create_zeroed(sizeof(DHCPv4Packet))) - { - auto* options = peek().options(); - // set the magic DHCP cookie value - options[0] = 99; - options[1] = 130; - options[2] = 83; - options[3] = 99; - } - - void add_option(DHCPOption option, u8 length, const void* data) - { - ASSERT(m_can_add); - // we need enough space to fit the option value, its length, and its data - ASSERT(next_option_offset + length + 2 < DHCPV4_OPTION_FIELD_MAX_LENGTH); - - auto* options = peek().options(); - options[next_option_offset++] = (u8)option; - memcpy(options + next_option_offset, &length, 1); - next_option_offset++; - memcpy(options + next_option_offset, data, length); - next_option_offset += length; - } - - void set_message_type(DHCPMessageType type) { add_option(DHCPOption::DHCPMessageType, 1, &type); } - - DHCPv4Packet& peek() { return *(DHCPv4Packet*)m_buffer.data(); } - DHCPv4Packet& build() - { - add_option(DHCPOption::End, 0, nullptr); - m_can_add = false; - return *(DHCPv4Packet*)m_buffer.data(); - } - size_t size() const { return m_buffer.size(); } - -private: - ByteBuffer m_buffer; - size_t next_option_offset { 4 }; - bool m_can_add { true }; -}; diff --git a/Services/DHCPClient/DHCPv4Client.cpp b/Services/DHCPClient/DHCPv4Client.cpp deleted file mode 100644 index 7a759ed427..0000000000 --- a/Services/DHCPClient/DHCPv4Client.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "DHCPv4Client.h" -#include <AK/ByteBuffer.h> -#include <AK/Endian.h> -#include <AK/Function.h> -#include <LibCore/SocketAddress.h> -#include <LibCore/Timer.h> -#include <stdio.h> - -//#define DHCPV4CLIENT_DEBUG - -static void send(const InterfaceDescriptor& iface, const DHCPv4Packet& packet, Core::Object*) -{ - int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) { - dbg() << "ERROR: socket :: " << strerror(errno); - return; - } - - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface.m_ifname.characters(), IFNAMSIZ) < 0) { - dbg() << "ERROR: setsockopt(SO_BINDTODEVICE) :: " << strerror(errno); - return; - } - - sockaddr_in dst; - memset(&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; - dst.sin_port = htons(67); - dst.sin_addr.s_addr = IPv4Address { 255, 255, 255, 255 }.to_u32(); - memset(&dst.sin_zero, 0, sizeof(dst.sin_zero)); - - auto rc = sendto(fd, &packet, sizeof(packet), 0, (sockaddr*)&dst, sizeof(dst)); - if (rc < 0) { - dbg() << "sendto failed with " << strerror(errno); - // FIXME: what do we do here? - } -} - -static void set_params(const InterfaceDescriptor& iface, const IPv4Address& ipv4_addr, const IPv4Address& netmask, const IPv4Address& gateway) -{ - int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (fd < 0) { - dbg() << "ERROR: socket :: " << strerror(errno); - return; - } - - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - - bool fits = iface.m_ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ); - if (!fits) { - dbgln("Interface name doesn't fit into IFNAMSIZ!"); - return; - } - - // set the IP address - ifr.ifr_addr.sa_family = AF_INET; - ((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr = ipv4_addr.to_in_addr_t(); - - if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { - dbg() << "ERROR: ioctl(SIOCSIFADDR) :: " << strerror(errno); - } - - // set the network mask - ((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr = netmask.to_in_addr_t(); - - if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) { - dbg() << "ERROR: ioctl(SIOCSIFNETMASK) :: " << strerror(errno); - } - - // set the default gateway - struct rtentry rt; - memset(&rt, 0, sizeof(rt)); - - rt.rt_dev = const_cast<char*>(iface.m_ifname.characters()); - rt.rt_gateway.sa_family = AF_INET; - ((sockaddr_in&)rt.rt_gateway).sin_addr.s_addr = gateway.to_in_addr_t(); - rt.rt_flags = RTF_UP | RTF_GATEWAY; - - if (ioctl(fd, SIOCADDRT, &rt) < 0) { - dbg() << "Error: ioctl(SIOCADDRT) :: " << strerror(errno); - } -} - -DHCPv4Client::DHCPv4Client(Vector<InterfaceDescriptor> ifnames) - : m_ifnames(ifnames) -{ - m_server = Core::UDPServer::construct(this); - m_server->on_ready_to_receive = [this] { - auto buffer = m_server->receive(sizeof(DHCPv4Packet)); - dbg() << "Received " << buffer.size() << " bytes"; - if (buffer.size() != sizeof(DHCPv4Packet)) { - dbg() << "we expected " << sizeof(DHCPv4Packet) << " bytes, this is a bad packet"; - return; - } - auto& packet = *(DHCPv4Packet*)buffer.data(); - process_incoming(packet); - }; - - if (!m_server->bind({}, 68)) { - dbgln("The server we just created somehow came already bound, refusing to continue"); - ASSERT_NOT_REACHED(); - } - - for (auto& iface : m_ifnames) - dhcp_discover(iface); -} - -DHCPv4Client::~DHCPv4Client() -{ -} - -void DHCPv4Client::handle_offer(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options) -{ - dbg() << "We were offered " << packet.yiaddr().to_string() << " for " << options.get<u32>(DHCPOption::IPAddressLeaseTime).value_or(0); - auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); - if (!transaction) { - dbg() << "we're not looking for " << packet.xid(); - return; - } - if (transaction->has_ip) - return; - if (transaction->accepted_offer) { - // we've accepted someone's offer, but they haven't given us an ack - // TODO: maybe record this offer? - return; - } - // TAKE IT... - transaction->offered_lease_time = options.get<u32>(DHCPOption::IPAddressLeaseTime).value(); - dhcp_request(*transaction, packet); -} - -void DHCPv4Client::handle_ack(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options) -{ -#ifdef DHCPV4CLIENT_DEBUG - dbg() << "The DHCP server handed us " << packet.yiaddr().to_string(); - dbg() << "Here are the options: " << options.to_string(); -#endif - auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); - if (!transaction) { - dbg() << "we're not looking for " << packet.xid(); - return; - } - transaction->has_ip = true; - auto& interface = transaction->interface; - auto new_ip = packet.yiaddr(); - auto lease_time = AK::convert_between_host_and_network_endian(options.get<u32>(DHCPOption::IPAddressLeaseTime).value_or(transaction->offered_lease_time)); - // set a timer for the duration of the lease, we shall renew if needed - Core::Timer::create_single_shot( - lease_time * 1000, - [this, transaction, interface = InterfaceDescriptor { interface }, new_ip] { - transaction->accepted_offer = false; - transaction->has_ip = false; - dhcp_discover(interface, new_ip); - }, - this); - set_params(transaction->interface, new_ip, options.get<IPv4Address>(DHCPOption::SubnetMask).value(), options.get_many<IPv4Address>(DHCPOption::Router, 1).first()); -} - -void DHCPv4Client::handle_nak(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options) -{ - dbg() << "The DHCP server told us to go chase our own tail about " << packet.yiaddr().to_string(); - dbg() << "Here are the options: " << options.to_string(); - // make another request a bit later :shrug: - auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); - if (!transaction) { - dbg() << "we're not looking for " << packet.xid(); - return; - } - transaction->accepted_offer = false; - transaction->has_ip = false; - auto& iface = transaction->interface; - Core::Timer::create_single_shot( - 10000, - [this, iface = InterfaceDescriptor { iface }] { - dhcp_discover(iface); - }, - this); -} - -void DHCPv4Client::process_incoming(const DHCPv4Packet& packet) -{ - auto options = packet.parse_options(); -#ifdef DHCPV4CLIENT_DEBUG - dbg() << "Here are the options: " << options.to_string(); -#endif - auto value = options.get<DHCPMessageType>(DHCPOption::DHCPMessageType).value(); - switch (value) { - case DHCPMessageType::DHCPOffer: - handle_offer(packet, options); - break; - case DHCPMessageType::DHCPAck: - handle_ack(packet, options); - break; - case DHCPMessageType::DHCPNak: - handle_nak(packet, options); - break; - case DHCPMessageType::DHCPDiscover: - case DHCPMessageType::DHCPRequest: - case DHCPMessageType::DHCPRelease: - // These are not for us - // we're just getting them because there are other people on our subnet - // broadcasting stuff - break; - case DHCPMessageType::DHCPDecline: - default: - dbg() << "I dunno what to do with this " << (u8)value; - ASSERT_NOT_REACHED(); - break; - } -} - -void DHCPv4Client::dhcp_discover(const InterfaceDescriptor& iface, IPv4Address previous) -{ - auto transaction_id = rand(); -#ifdef DHCPV4CLIENT_DEBUG - dbg() << "Trying to lease an IP for " << iface.m_ifname << " with ID " << transaction_id; - if (!previous.is_zero()) - dbg() << "going to request the server to hand us " << previous.to_string(); -#endif - DHCPv4PacketBuilder builder; - - DHCPv4Packet& packet = builder.peek(); - packet.set_op(DHCPv4Op::BootRequest); - packet.set_htype(1); // 10mb ethernet - packet.set_hlen(sizeof(MACAddress)); - packet.set_xid(transaction_id); - packet.set_flags(DHCPv4Flags::Broadcast); - packet.ciaddr() = previous; - packet.set_chaddr(iface.m_mac_address); - packet.set_secs(65535); // we lie - - // set packet options - builder.set_message_type(DHCPMessageType::DHCPDiscover); - auto& dhcp_packet = builder.build(); - - // broadcast the discover request - send(iface, dhcp_packet, this); - m_ongoing_transactions.set(transaction_id, make<DHCPv4Transaction>(iface)); -} - -void DHCPv4Client::dhcp_request(DHCPv4Transaction& transaction, const DHCPv4Packet& offer) -{ - auto& iface = transaction.interface; - dbg() << "Leasing the IP " << offer.yiaddr().to_string() << " for adapter " << iface.m_ifname; - DHCPv4PacketBuilder builder; - - DHCPv4Packet& packet = builder.peek(); - packet.set_op(DHCPv4Op::BootRequest); - packet.set_htype(1); // 10mb ethernet - packet.set_hlen(sizeof(MACAddress)); - packet.set_xid(offer.xid()); - packet.set_flags(DHCPv4Flags::Broadcast); - packet.set_chaddr(iface.m_mac_address); - packet.set_secs(65535); // we lie - - // set packet options - builder.set_message_type(DHCPMessageType::DHCPRequest); - auto& dhcp_packet = builder.build(); - - // broadcast the "request" request - send(iface, dhcp_packet, this); - transaction.accepted_offer = true; -} diff --git a/Services/DHCPClient/DHCPv4Client.h b/Services/DHCPClient/DHCPv4Client.h deleted file mode 100644 index 60f76a55ef..0000000000 --- a/Services/DHCPClient/DHCPv4Client.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "DHCPv4.h" -#include <AK/HashMap.h> -#include <AK/OwnPtr.h> -#include <AK/String.h> -#include <AK/Vector.h> -#include <LibCore/UDPServer.h> -#include <net/if.h> -#include <net/route.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -struct InterfaceDescriptor { - String m_ifname; - MACAddress m_mac_address; -}; - -struct DHCPv4Transaction { - DHCPv4Transaction(InterfaceDescriptor ifname) - : interface(ifname) - { - } - - InterfaceDescriptor interface; - bool accepted_offer { false }; - bool has_ip { false }; - u32 offered_lease_time { 0 }; -}; - -class DHCPv4Client final : public Core::Object { - C_OBJECT(DHCPv4Client) - -public: - explicit DHCPv4Client(Vector<InterfaceDescriptor> ifnames); - virtual ~DHCPv4Client() override; - - void dhcp_discover(const InterfaceDescriptor& ifname, IPv4Address previous = IPv4Address { 0, 0, 0, 0 }); - void dhcp_request(DHCPv4Transaction& transaction, const DHCPv4Packet& packet); - - void process_incoming(const DHCPv4Packet& packet); - - bool id_is_registered(u32 id) { return m_ongoing_transactions.contains(id); } - -private: - HashMap<u32, OwnPtr<DHCPv4Transaction>> m_ongoing_transactions; - Vector<InterfaceDescriptor> m_ifnames; - RefPtr<Core::UDPServer> m_server; - - void handle_offer(const DHCPv4Packet&, const ParsedDHCPv4Options&); - void handle_ack(const DHCPv4Packet&, const ParsedDHCPv4Options&); - void handle_nak(const DHCPv4Packet&, const ParsedDHCPv4Options&); -}; diff --git a/Services/DHCPClient/main.cpp b/Services/DHCPClient/main.cpp deleted file mode 100644 index d81750b299..0000000000 --- a/Services/DHCPClient/main.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "DHCPv4Client.h" -#include <AK/JsonArray.h> -#include <AK/JsonObject.h> -#include <AK/String.h> -#include <AK/Types.h> -#include <LibCore/EventLoop.h> -#include <LibCore/File.h> -#include <LibCore/LocalServer.h> -#include <stdio.h> -#include <string.h> - -static u8 mac_part(const Vector<String>& parts, size_t index) -{ - auto chars = parts.at(index).characters(); - return (chars[0] - '0') * 16 + (chars[1] - '0'); -} - -static MACAddress mac_from_string(const String& str) -{ - auto chunks = str.split(':'); - ASSERT(chunks.size() == 6); // should we...worry about this? - return { - mac_part(chunks, 0), mac_part(chunks, 1), mac_part(chunks, 2), - mac_part(chunks, 3), mac_part(chunks, 4), mac_part(chunks, 5) - }; -} - -int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) -{ - if (pledge("stdio unix inet cpath rpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - Core::EventLoop event_loop; - - if (unveil("/proc/net/", "r") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - auto file = Core::File::construct("/proc/net/adapters"); - if (!file->open(Core::IODevice::ReadOnly)) { - fprintf(stderr, "Error: %s\n", file->error_string()); - return 1; - } - - auto file_contents = file->read_all(); - auto json = JsonValue::from_string(file_contents); - ASSERT(json.has_value()); - Vector<InterfaceDescriptor> ifnames; - json.value().as_array().for_each([&ifnames](auto& value) { - auto if_object = value.as_object(); - - if (if_object.get("class_name").to_string() == "LoopbackAdapter") - return; - - auto name = if_object.get("name").to_string(); - auto mac = if_object.get("mac_address").to_string(); - ifnames.append({ name, mac_from_string(mac) }); - }); - - auto client = DHCPv4Client::construct(move(ifnames)); - - if (pledge("stdio inet", nullptr) < 0) { - perror("pledge"); - return 1; - } - - return event_loop.exec(); -} diff --git a/Services/EchoServer/CMakeLists.txt b/Services/EchoServer/CMakeLists.txt deleted file mode 100644 index e7d67fe14e..0000000000 --- a/Services/EchoServer/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SOURCES - Client.cpp - main.cpp -) - -serenity_bin(EchoServer) -target_link_libraries(EchoServer LibCore) diff --git a/Services/EchoServer/Client.cpp b/Services/EchoServer/Client.cpp deleted file mode 100644 index 874553915b..0000000000 --- a/Services/EchoServer/Client.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Client.h" - -Client::Client(int id, RefPtr<Core::TCPSocket> socket) - : m_id(id) - , m_socket(move(socket)) -{ - m_socket->on_ready_to_read = [this] { drain_socket(); }; -} - -void Client::drain_socket() -{ - NonnullRefPtr<Client> protect(*this); - while (m_socket->can_read()) { - auto buf = m_socket->read(1024); - - dbg() << "Read " << buf.size() << " bytes: " << buf; - - if (m_socket->eof()) { - quit(); - break; - } - - m_socket->write(buf); - } -} - -void Client::quit() -{ - m_socket->close(); - if (on_exit) - on_exit(); -} diff --git a/Services/EchoServer/Client.h b/Services/EchoServer/Client.h deleted file mode 100644 index 23359fa79b..0000000000 --- a/Services/EchoServer/Client.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <LibCore/TCPSocket.h> - -class Client : public RefCounted<Client> { -public: - static NonnullRefPtr<Client> create(int id, RefPtr<Core::TCPSocket> socket) - { - return adopt(*new Client(id, move(socket))); - } - - Function<void()> on_exit; - -protected: - Client(int id, RefPtr<Core::TCPSocket> socket); - - void drain_socket(); - void quit(); - -private: - int m_id { 0 }; - RefPtr<Core::TCPSocket> m_socket; -}; diff --git a/Services/EchoServer/main.cpp b/Services/EchoServer/main.cpp deleted file mode 100644 index a4136c3df6..0000000000 --- a/Services/EchoServer/main.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Client.h" -#include <AK/HashMap.h> -#include <AK/IPv4Address.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/EventLoop.h> -#include <LibCore/TCPServer.h> -#include <LibCore/TCPSocket.h> -#include <stdio.h> -#include <stdlib.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio cpath unix fattr inet id accept", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/tmp/rpc", "rwc") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - int port = 7; - - Core::ArgsParser args_parser; - args_parser.add_option(port, "Port to listen on", "port", 'p', "port"); - args_parser.parse(argc, argv); - - if ((u16)port != port) { - warnln("Invalid port number: {}", port); - exit(1); - } - - Core::EventLoop event_loop; - - auto server = Core::TCPServer::construct(); - - if (!server->listen({}, port)) { - warnln("Listening on 0.0.0.0:{} failed", port); - exit(1); - } - - HashMap<int, NonnullRefPtr<Client>> clients; - int next_id = 0; - - server->on_ready_to_accept = [&next_id, &clients, &server] { - int id = next_id++; - - auto client_socket = server->accept(); - if (!client_socket) { - perror("accept"); - return; - } - - outln("Client {} connected", id); - - auto client = Client::create(id, move(client_socket)); - client->on_exit = [&clients, id] { - clients.remove(id); - outln("Client {} disconnected", id); - }; - clients.set(id, client); - }; - - outln("Listening on 0.0.0.0:{}", port); - - return event_loop.exec(); -} diff --git a/Services/ImageDecoder/CMakeLists.txt b/Services/ImageDecoder/CMakeLists.txt deleted file mode 100644 index 56482fe31d..0000000000 --- a/Services/ImageDecoder/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -compile_ipc(ImageDecoderServer.ipc ImageDecoderServerEndpoint.h) -compile_ipc(ImageDecoderClient.ipc ImageDecoderClientEndpoint.h) - -set(SOURCES - ClientConnection.cpp - main.cpp - ImageDecoderServerEndpoint.h - ImageDecoderClientEndpoint.h -) - -serenity_bin(ImageDecoder) -target_link_libraries(ImageDecoder LibGfx LibIPC) diff --git a/Services/ImageDecoder/ClientConnection.cpp b/Services/ImageDecoder/ClientConnection.cpp deleted file mode 100644 index 1e516ccdb9..0000000000 --- a/Services/ImageDecoder/ClientConnection.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Badge.h> -#include <AK/SharedBuffer.h> -#include <ImageDecoder/ClientConnection.h> -#include <ImageDecoder/ImageDecoderClientEndpoint.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/ImageDecoder.h> -#include <LibGfx/SystemTheme.h> - -namespace ImageDecoder { - -static HashMap<int, RefPtr<ClientConnection>> s_connections; - -ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id) - : IPC::ClientConnection<ImageDecoderClientEndpoint, ImageDecoderServerEndpoint>(*this, move(socket), client_id) -{ - s_connections.set(client_id, *this); -} - -ClientConnection::~ClientConnection() -{ -} - -void ClientConnection::die() -{ - s_connections.remove(client_id()); - exit(0); -} - -OwnPtr<Messages::ImageDecoderServer::GreetResponse> ClientConnection::handle(const Messages::ImageDecoderServer::Greet& message) -{ - set_client_pid(message.client_pid()); - return make<Messages::ImageDecoderServer::GreetResponse>(client_id(), getpid()); -} - -OwnPtr<Messages::ImageDecoderServer::DecodeImageResponse> ClientConnection::handle(const Messages::ImageDecoderServer::DecodeImage& message) -{ - auto encoded_buffer = SharedBuffer::create_from_shbuf_id(message.encoded_shbuf_id()); - if (!encoded_buffer) { -#ifdef IMAGE_DECODER_DEBUG - dbgln("Could not map encoded data buffer"); -#endif - return {}; - } - - if (message.encoded_size() > (size_t)encoded_buffer->size()) { -#ifdef IMAGE_DECODER_DEBUG - dbgln("Encoded buffer is smaller than encoded size"); -#endif - return {}; - } - -#ifdef IMAGE_DECODER_DEBUG - dbg() << "Trying to decode " << message.encoded_size() << " bytes of image(?) data in shbuf_id=" << message.encoded_shbuf_id() << " (shbuf size: " << encoded_buffer->size() << ")"; -#endif - - auto decoder = Gfx::ImageDecoder::create(encoded_buffer->data<u8>(), message.encoded_size()); - auto bitmap = decoder->bitmap(); - - if (!bitmap) { -#ifdef IMAGE_DECODER_DEBUG - dbgln("Could not decode image from encoded data"); -#endif - return make<Messages::ImageDecoderServer::DecodeImageResponse>(-1, Gfx::IntSize(), (i32)Gfx::BitmapFormat::Invalid, Vector<u32>()); - } - - // FIXME: We should fix ShareableBitmap so you can send it in responses as well as requests.. - m_shareable_bitmap = bitmap->to_bitmap_backed_by_shared_buffer(); - m_shareable_bitmap->shared_buffer()->share_with(client_pid()); - Vector<u32> palette; - if (m_shareable_bitmap->is_indexed()) { - palette = m_shareable_bitmap->palette_to_vector(); - } - return make<Messages::ImageDecoderServer::DecodeImageResponse>(m_shareable_bitmap->shbuf_id(), m_shareable_bitmap->size(), (i32)m_shareable_bitmap->format(), palette); -} - -} diff --git a/Services/ImageDecoder/ClientConnection.h b/Services/ImageDecoder/ClientConnection.h deleted file mode 100644 index 1447c3b063..0000000000 --- a/Services/ImageDecoder/ClientConnection.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/HashMap.h> -#include <ImageDecoder/Forward.h> -#include <ImageDecoder/ImageDecoderClientEndpoint.h> -#include <ImageDecoder/ImageDecoderServerEndpoint.h> -#include <LibIPC/ClientConnection.h> -#include <LibWeb/Forward.h> - -namespace ImageDecoder { - -class ClientConnection final - : public IPC::ClientConnection<ImageDecoderClientEndpoint, ImageDecoderServerEndpoint> - , public ImageDecoderServerEndpoint { - C_OBJECT(ClientConnection); - -public: - explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); - ~ClientConnection() override; - - virtual void die() override; - -private: - virtual OwnPtr<Messages::ImageDecoderServer::GreetResponse> handle(const Messages::ImageDecoderServer::Greet&) override; - virtual OwnPtr<Messages::ImageDecoderServer::DecodeImageResponse> handle(const Messages::ImageDecoderServer::DecodeImage&) override; - - RefPtr<Gfx::Bitmap> m_shareable_bitmap; -}; - -} diff --git a/Services/ImageDecoder/Forward.h b/Services/ImageDecoder/Forward.h deleted file mode 100644 index e43ca83960..0000000000 --- a/Services/ImageDecoder/Forward.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -namespace WebContent { - -class ClientConnection; -class PageHost; - -} diff --git a/Services/ImageDecoder/ImageDecoderClient.ipc b/Services/ImageDecoder/ImageDecoderClient.ipc deleted file mode 100644 index 49dc3862c2..0000000000 --- a/Services/ImageDecoder/ImageDecoderClient.ipc +++ /dev/null @@ -1,4 +0,0 @@ -endpoint ImageDecoderClient = 7002 -{ - Dummy() =| -} diff --git a/Services/ImageDecoder/ImageDecoderServer.ipc b/Services/ImageDecoder/ImageDecoderServer.ipc deleted file mode 100644 index 295454282f..0000000000 --- a/Services/ImageDecoder/ImageDecoderServer.ipc +++ /dev/null @@ -1,7 +0,0 @@ -endpoint ImageDecoderServer = 7001 -{ - Greet(i32 client_pid) => (i32 client_id, i32 server_pid) - - DecodeImage(i32 encoded_shbuf_id, u32 encoded_size) => (i32 decoded_shbuf_id, Gfx::IntSize size, i32 bitmap_format, Vector<u32> palette) - -} diff --git a/Services/ImageDecoder/main.cpp b/Services/ImageDecoder/main.cpp deleted file mode 100644 index d370f0d00b..0000000000 --- a/Services/ImageDecoder/main.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <ImageDecoder/ClientConnection.h> -#include <LibCore/EventLoop.h> -#include <LibCore/LocalServer.h> -#include <LibIPC/ClientConnection.h> - -int main(int, char**) -{ - Core::EventLoop event_loop; - if (pledge("stdio shared_buffer unix", nullptr) < 0) { - perror("pledge"); - return 1; - } - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server(); - IPC::new_client_connection<ImageDecoder::ClientConnection>(socket.release_nonnull(), 1); - if (pledge("stdio shared_buffer", nullptr) < 0) { - perror("pledge"); - return 1; - } - return event_loop.exec(); -} diff --git a/Services/LaunchServer/CMakeLists.txt b/Services/LaunchServer/CMakeLists.txt deleted file mode 100644 index 29308ce7f1..0000000000 --- a/Services/LaunchServer/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -compile_ipc(LaunchServer.ipc LaunchServerEndpoint.h) -compile_ipc(LaunchClient.ipc LaunchClientEndpoint.h) - -set(SOURCES - ClientConnection.cpp - Launcher.cpp - main.cpp - LaunchClientEndpoint.h - LaunchServerEndpoint.h -) - -serenity_bin(LaunchServer) -target_link_libraries(LaunchServer LibCore LibIPC LibDesktop) diff --git a/Services/LaunchServer/ClientConnection.cpp b/Services/LaunchServer/ClientConnection.cpp deleted file mode 100644 index 7ac167c1ae..0000000000 --- a/Services/LaunchServer/ClientConnection.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ClientConnection.h" -#include "Launcher.h" -#include <AK/HashMap.h> -#include <AK/URL.h> -#include <LaunchServer/LaunchClientEndpoint.h> - -namespace LaunchServer { - -static HashMap<int, RefPtr<ClientConnection>> s_connections; -ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id) - : IPC::ClientConnection<LaunchClientEndpoint, LaunchServerEndpoint>(*this, move(client_socket), client_id) -{ - s_connections.set(client_id, *this); -} - -ClientConnection::~ClientConnection() -{ -} - -void ClientConnection::die() -{ - s_connections.remove(client_id()); -} - -OwnPtr<Messages::LaunchServer::GreetResponse> ClientConnection::handle(const Messages::LaunchServer::Greet&) -{ - return make<Messages::LaunchServer::GreetResponse>(client_id()); -} - -OwnPtr<Messages::LaunchServer::OpenURLResponse> ClientConnection::handle(const Messages::LaunchServer::OpenURL& request) -{ - if (!m_allowlist.is_empty()) { - bool allowed = false; - for (auto& allowed_handler : m_allowlist) { - if (allowed_handler.handler_name == request.handler_name() - && (allowed_handler.any_url || allowed_handler.urls.contains_slow(request.url()))) { - allowed = true; - break; - } - } - if (!allowed) { - // You are not on the list, go home! - did_misbehave(String::formatted("Client requested a combination of handler/URL that was not on the list: '{}' with '{}'", request.handler_name(), request.url()).characters()); - return {}; - } - } - - URL url(request.url()); - auto result = Launcher::the().open_url(url, request.handler_name()); - return make<Messages::LaunchServer::OpenURLResponse>(result); -} - -OwnPtr<Messages::LaunchServer::GetHandlersForURLResponse> ClientConnection::handle(const Messages::LaunchServer::GetHandlersForURL& request) -{ - URL url(request.url()); - auto result = Launcher::the().handlers_for_url(url); - return make<Messages::LaunchServer::GetHandlersForURLResponse>(result); -} - -OwnPtr<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse> ClientConnection::handle(const Messages::LaunchServer::GetHandlersWithDetailsForURL& request) -{ - URL url(request.url()); - auto result = Launcher::the().handlers_with_details_for_url(url); - return make<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse>(result); -} - -OwnPtr<Messages::LaunchServer::AddAllowedURLResponse> ClientConnection::handle(const Messages::LaunchServer::AddAllowedURL& request) -{ - if (m_allowlist_is_sealed) { - did_misbehave("Got request to add more allowed handlers after list was sealed"); - return {}; - } - - if (!request.url().is_valid()) { - did_misbehave("Got request to allow invalid URL"); - return {}; - } - - m_allowlist.empend(String(), false, Vector<URL> { request.url() }); - - return make<Messages::LaunchServer::AddAllowedURLResponse>(); -} - -OwnPtr<Messages::LaunchServer::AddAllowedHandlerWithAnyURLResponse> ClientConnection::handle(const Messages::LaunchServer::AddAllowedHandlerWithAnyURL& request) -{ - if (m_allowlist_is_sealed) { - did_misbehave("Got request to add more allowed handlers after list was sealed"); - return {}; - } - - if (request.handler_name().is_empty()) { - did_misbehave("Got request to allow empty handler name"); - return {}; - } - - m_allowlist.empend(request.handler_name(), true, Vector<URL>()); - - return make<Messages::LaunchServer::AddAllowedHandlerWithAnyURLResponse>(); -} - -OwnPtr<Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLsResponse> ClientConnection::handle(const Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLs& request) -{ - if (m_allowlist_is_sealed) { - did_misbehave("Got request to add more allowed handlers after list was sealed"); - return {}; - } - - if (request.handler_name().is_empty()) { - did_misbehave("Got request to allow empty handler name"); - return {}; - } - - if (request.urls().is_empty()) { - did_misbehave("Got request to allow empty URL list"); - return {}; - } - - m_allowlist.empend(request.handler_name(), false, request.urls()); - - return make<Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLsResponse>(); -} - -OwnPtr<Messages::LaunchServer::SealAllowlistResponse> ClientConnection::handle(const Messages::LaunchServer::SealAllowlist&) -{ - if (m_allowlist_is_sealed) { - did_misbehave("Got more than one request to seal the allowed handlers list"); - return {}; - } - - return make<Messages::LaunchServer::SealAllowlistResponse>(); -} - -} diff --git a/Services/LaunchServer/ClientConnection.h b/Services/LaunchServer/ClientConnection.h deleted file mode 100644 index 77eef53374..0000000000 --- a/Services/LaunchServer/ClientConnection.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <LaunchServer/LaunchClientEndpoint.h> -#include <LaunchServer/LaunchServerEndpoint.h> -#include <LibIPC/ClientConnection.h> - -namespace LaunchServer { - -class ClientConnection final : public IPC::ClientConnection<LaunchClientEndpoint, LaunchServerEndpoint> - , public LaunchServerEndpoint { - C_OBJECT(ClientConnection) -public: - ~ClientConnection() override; - - virtual void die() override; - -private: - explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); - - virtual OwnPtr<Messages::LaunchServer::GreetResponse> handle(const Messages::LaunchServer::Greet&) override; - virtual OwnPtr<Messages::LaunchServer::OpenURLResponse> handle(const Messages::LaunchServer::OpenURL&) override; - virtual OwnPtr<Messages::LaunchServer::GetHandlersForURLResponse> handle(const Messages::LaunchServer::GetHandlersForURL&) override; - virtual OwnPtr<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse> handle(const Messages::LaunchServer::GetHandlersWithDetailsForURL&) override; - virtual OwnPtr<Messages::LaunchServer::AddAllowedURLResponse> handle(const Messages::LaunchServer::AddAllowedURL&) override; - virtual OwnPtr<Messages::LaunchServer::AddAllowedHandlerWithAnyURLResponse> handle(const Messages::LaunchServer::AddAllowedHandlerWithAnyURL&) override; - virtual OwnPtr<Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLsResponse> handle(const Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLs&) override; - virtual OwnPtr<Messages::LaunchServer::SealAllowlistResponse> handle(const Messages::LaunchServer::SealAllowlist&) override; - - struct AllowlistEntry { - String handler_name; - bool any_url { false }; - Vector<URL> urls; - }; - - Vector<AllowlistEntry> m_allowlist; - bool m_allowlist_is_sealed { false }; -}; -} diff --git a/Services/LaunchServer/LaunchClient.ipc b/Services/LaunchServer/LaunchClient.ipc deleted file mode 100644 index abd4e9ba3d..0000000000 --- a/Services/LaunchServer/LaunchClient.ipc +++ /dev/null @@ -1,4 +0,0 @@ -endpoint LaunchClient = 102 -{ - Dummy() =| -} diff --git a/Services/LaunchServer/LaunchServer.ipc b/Services/LaunchServer/LaunchServer.ipc deleted file mode 100644 index 4f50221f4b..0000000000 --- a/Services/LaunchServer/LaunchServer.ipc +++ /dev/null @@ -1,12 +0,0 @@ -endpoint LaunchServer = 101 -{ - Greet() => (i32 client_id) - OpenURL(URL url, String handler_name) => (bool response) - GetHandlersForURL(URL url) => (Vector<String> handlers) - GetHandlersWithDetailsForURL(URL url) => (Vector<String> handlers_details) - - AddAllowedURL(URL url) => () - AddAllowedHandlerWithAnyURL(String handler_name) => () - AddAllowedHandlerWithOnlySpecificURLs(String handler_name, Vector<URL> urls) => () - SealAllowlist() => () -} diff --git a/Services/LaunchServer/Launcher.cpp b/Services/LaunchServer/Launcher.cpp deleted file mode 100644 index ad6d025035..0000000000 --- a/Services/LaunchServer/Launcher.cpp +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk>, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Launcher.h" -#include <AK/Function.h> -#include <AK/JsonObject.h> -#include <AK/JsonObjectSerializer.h> -#include <AK/JsonValue.h> -#include <AK/LexicalPath.h> -#include <AK/StringBuilder.h> -#include <LibCore/ConfigFile.h> -#include <LibDesktop/AppFile.h> -#include <serenity.h> -#include <spawn.h> -#include <stdio.h> -#include <sys/stat.h> - -namespace LaunchServer { - -static Launcher* s_the; -static bool spawn(String executable, String argument); - -String Handler::name_from_executable(const StringView& executable) -{ - auto separator = executable.find_last_of('/'); - if (separator.has_value()) { - auto start = separator.value() + 1; - return executable.substring_view(start, executable.length() - start); - } - return executable; -} - -void Handler::from_executable(Type handler_type, const String& executable) -{ - this->handler_type = handler_type; - this->name = name_from_executable(executable); - this->executable = executable; -} - -String Handler::to_details_str() const -{ - StringBuilder builder; - JsonObjectSerializer obj { builder }; - obj.add("executable", executable); - obj.add("name", name); - switch (handler_type) { - case Type::Application: - obj.add("type", "app"); - break; - case Type::UserDefault: - obj.add("type", "userdefault"); - break; - case Type::UserPreferred: - obj.add("type", "userpreferred"); - break; - default: - break; - } - obj.finish(); - return builder.build(); -} - -Launcher::Launcher() -{ - ASSERT(s_the == nullptr); - s_the = this; -} - -Launcher& Launcher::the() -{ - ASSERT(s_the); - return *s_the; -} - -void Launcher::load_handlers(const String& af_dir) -{ - Desktop::AppFile::for_each([&](auto af) { - auto app_name = af->name(); - auto app_executable = af->executable(); - HashTable<String> file_types; - for (auto& file_type : af->launcher_file_types()) - file_types.set(file_type); - HashTable<String> protocols; - for (auto& protocol : af->launcher_protocols()) - protocols.set(protocol); - m_handlers.set(app_executable, { Handler::Type::Default, app_name, app_executable, file_types, protocols }); - }, - af_dir); -} - -void Launcher::load_config(const Core::ConfigFile& cfg) -{ - for (auto key : cfg.keys("FileType")) { - auto handler = cfg.read_entry("FileType", key).trim_whitespace(); - if (handler.is_empty()) - continue; - m_file_handlers.set(key.to_lowercase(), handler); - } - - for (auto key : cfg.keys("Protocol")) { - auto handler = cfg.read_entry("Protocol", key).trim_whitespace(); - if (handler.is_empty()) - continue; - m_protocol_handlers.set(key.to_lowercase(), handler); - } -} - -Vector<String> Launcher::handlers_for_url(const URL& url) -{ - Vector<String> handlers; - if (url.protocol() == "file") { - for_each_handler_for_path(url.path(), [&](auto& handler) -> bool { - handlers.append(handler.executable); - return true; - }); - } else { - for_each_handler(url.protocol(), m_protocol_handlers, [&](const auto& handler) -> bool { - if (handler.handler_type != Handler::Type::Default || handler.protocols.contains(url.protocol())) { - handlers.append(handler.executable); - return true; - } - return false; - }); - } - return handlers; -} - -Vector<String> Launcher::handlers_with_details_for_url(const URL& url) -{ - Vector<String> handlers; - if (url.protocol() == "file") { - for_each_handler_for_path(url.path(), [&](auto& handler) -> bool { - handlers.append(handler.to_details_str()); - return true; - }); - } else { - for_each_handler(url.protocol(), m_protocol_handlers, [&](const auto& handler) -> bool { - if (handler.handler_type != Handler::Type::Default || handler.protocols.contains(url.protocol())) { - handlers.append(handler.to_details_str()); - return true; - } - return false; - }); - } - return handlers; -} - -bool Launcher::open_url(const URL& url, const String& handler_name) -{ - if (!handler_name.is_null()) - return open_with_handler_name(url, handler_name); - - if (url.protocol() == "file") - return open_file_url(url); - - return open_with_user_preferences(m_protocol_handlers, url.protocol(), url.to_string()); -} - -bool Launcher::open_with_handler_name(const URL& url, const String& handler_name) -{ - auto handler_optional = m_handlers.get(handler_name); - if (!handler_optional.has_value()) - return false; - - auto& handler = handler_optional.value(); - String argument; - if (url.protocol() == "file") - argument = url.path(); - else - argument = url.to_string(); - return spawn(handler.executable, argument); -} - -bool spawn(String executable, String argument) -{ - pid_t child_pid; - const char* argv[] = { executable.characters(), argument.characters(), nullptr }; - if ((errno = posix_spawn(&child_pid, executable.characters(), nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - return false; - } else { - if (disown(child_pid) < 0) - perror("disown"); - } - return true; -} - -Handler Launcher::get_handler_for_executable(Handler::Type handler_type, const String& executable) const -{ - Handler handler; - auto existing_handler = m_handlers.get(executable); - if (existing_handler.has_value()) { - handler = existing_handler.value(); - handler.handler_type = handler_type; - } else { - handler.from_executable(handler_type, executable); - } - return handler; -} - -bool Launcher::open_with_user_preferences(const HashMap<String, String>& user_preferences, const String key, const String argument, const String default_program) -{ - auto program_path = user_preferences.get(key); - if (program_path.has_value()) - return spawn(program_path.value(), argument); - - // There wasn't a handler for this, so try the fallback instead - program_path = user_preferences.get("*"); - if (program_path.has_value()) - return spawn(program_path.value(), argument); - - // Absolute worst case, try the provided default program, if any - if (!default_program.is_empty()) - return spawn(default_program, argument); - - return false; -} - -void Launcher::for_each_handler(const String& key, HashMap<String, String>& user_preference, Function<bool(const Handler&)> f) -{ - auto user_preferred = user_preference.get(key); - if (user_preferred.has_value()) - f(get_handler_for_executable(Handler::Type::UserPreferred, user_preferred.value())); - - size_t counted = 0; - for (auto& handler : m_handlers) { - // Skip over the existing item in the list - if (user_preferred.has_value() && user_preferred.value() == handler.value.executable) - continue; - if (f(handler.value)) - counted++; - } - - auto user_default = user_preference.get("*"); - if (counted == 0 && user_default.has_value()) - f(get_handler_for_executable(Handler::Type::UserDefault, user_default.value())); -} - -void Launcher::for_each_handler_for_path(const String& path, Function<bool(const Handler&)> f) -{ - struct stat st; - if (stat(path.characters(), &st) < 0) { - perror("stat"); - return; - } - - // TODO: Make directory opening configurable - if (S_ISDIR(st.st_mode)) { - f(get_handler_for_executable(Handler::Type::Default, "/bin/FileManager")); - return; - } - - if ((st.st_mode & S_IFMT) == S_IFREG && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) - f(get_handler_for_executable(Handler::Type::Application, path)); - - auto extension = LexicalPath(path).extension().to_lowercase(); - - for_each_handler(extension, m_file_handlers, [&](const auto& handler) -> bool { - if (handler.handler_type != Handler::Type::Default || handler.file_types.contains(extension)) - return f(handler); - return false; - }); -} - -bool Launcher::open_file_url(const URL& url) -{ - struct stat st; - if (stat(url.path().characters(), &st) < 0) { - perror("stat"); - return false; - } - - // TODO: Make directory opening configurable - if (S_ISDIR(st.st_mode)) - return spawn("/bin/FileManager", url.path()); - - if ((st.st_mode & S_IFMT) == S_IFREG && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) - return spawn(url.path(), {}); - - auto extension_parts = url.path().to_lowercase().split('.'); - String extension = {}; - if (extension_parts.size() > 1) - extension = extension_parts.last(); - return open_with_user_preferences(m_file_handlers, extension, url.path(), "/bin/TextEditor"); -} -} diff --git a/Services/LaunchServer/Launcher.h b/Services/LaunchServer/Launcher.h deleted file mode 100644 index 8708cdc258..0000000000 --- a/Services/LaunchServer/Launcher.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/HashMap.h> -#include <AK/HashTable.h> -#include <AK/URL.h> -#include <LibCore/ConfigFile.h> -#include <LibDesktop/AppFile.h> - -namespace LaunchServer { - -struct Handler { - enum class Type { - Default = 0, - Application, - UserPreferred, - UserDefault - }; - Type handler_type; - String name; - String executable; - HashTable<String> file_types {}; - HashTable<String> protocols {}; - - static String name_from_executable(const StringView&); - void from_executable(Type, const String&); - String to_details_str() const; -}; - -class Launcher { -public: - Launcher(); - static Launcher& the(); - - void load_handlers(const String& af_dir = Desktop::AppFile::APP_FILES_DIRECTORY); - void load_config(const Core::ConfigFile&); - bool open_url(const URL&, const String& handler_name); - Vector<String> handlers_for_url(const URL&); - Vector<String> handlers_with_details_for_url(const URL&); - -private: - HashMap<String, Handler> m_handlers; - HashMap<String, String> m_protocol_handlers; - HashMap<String, String> m_file_handlers; - - Handler get_handler_for_executable(Handler::Type, const String&) const; - void for_each_handler(const String& key, HashMap<String, String>& user_preferences, Function<bool(const Handler&)> f); - void for_each_handler_for_path(const String&, Function<bool(const Handler&)> f); - bool open_file_url(const URL&); - bool open_with_user_preferences(const HashMap<String, String>& user_preferences, const String key, const String argument, const String default_program = {}); - bool open_with_handler_name(const URL&, const String& handler_name); -}; -} diff --git a/Services/LaunchServer/main.cpp b/Services/LaunchServer/main.cpp deleted file mode 100644 index 2535d1e8bb..0000000000 --- a/Services/LaunchServer/main.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ClientConnection.h" -#include "Launcher.h" -#include <LibCore/ConfigFile.h> -#include <LibCore/EventLoop.h> -#include <LibCore/LocalServer.h> -#include <stdio.h> -#include <unistd.h> - -int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) -{ - Core::EventLoop event_loop; - auto server = Core::LocalServer::construct(); - - auto launcher = LaunchServer::Launcher(); - - launcher.load_handlers(); - launcher.load_config(Core::ConfigFile::get_for_app("LaunchServer")); - - if (pledge("stdio accept rpath proc exec", nullptr) < 0) { - perror("pledge"); - return 1; - } - - bool ok = server->take_over_from_system_server(); - ASSERT(ok); - server->on_ready_to_accept = [&] { - auto client_socket = server->accept(); - if (!client_socket) { - dbgln("LaunchServer: accept failed."); - return; - } - static int s_next_client_id = 0; - int client_id = ++s_next_client_id; - dbgln("Received connection"); - IPC::new_client_connection<LaunchServer::ClientConnection>(client_socket.release_nonnull(), client_id); - }; - - return event_loop.exec(); -} diff --git a/Services/LookupServer/CMakeLists.txt b/Services/LookupServer/CMakeLists.txt deleted file mode 100644 index 89769d4abc..0000000000 --- a/Services/LookupServer/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(SOURCES - DNSAnswer.cpp - DNSRequest.cpp - DNSResponse.cpp - LookupServer.cpp - main.cpp -) - -serenity_bin(LookupServer) -target_link_libraries(LookupServer LibCore) diff --git a/Services/LookupServer/DNSAnswer.cpp b/Services/LookupServer/DNSAnswer.cpp deleted file mode 100644 index 775c23784e..0000000000 --- a/Services/LookupServer/DNSAnswer.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "DNSAnswer.h" -#include <time.h> - -DNSAnswer::DNSAnswer(const String& name, u16 type, u16 class_code, u32 ttl, const String& record_data) - : m_name(name) - , m_type(type) - , m_class_code(class_code) - , m_ttl(ttl) - , m_record_data(record_data) -{ - auto now = time(nullptr); - m_expiration_time = now + m_ttl; - if (m_expiration_time < now) - m_expiration_time = 0; -} - -bool DNSAnswer::has_expired() const -{ - return time(nullptr) >= m_expiration_time; -} diff --git a/Services/LookupServer/DNSAnswer.h b/Services/LookupServer/DNSAnswer.h deleted file mode 100644 index 794603dd30..0000000000 --- a/Services/LookupServer/DNSAnswer.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/String.h> -#include <AK/Types.h> - -class DNSAnswer { -public: - DNSAnswer(const String& name, u16 type, u16 class_code, u32 ttl, const String& record_data); - - const String& name() const { return m_name; } - u16 type() const { return m_type; } - u16 class_code() const { return m_class_code; } - u32 ttl() const { return m_ttl; } - const String& record_data() const { return m_record_data; } - - bool has_expired() const; - -private: - String m_name; - u16 m_type { 0 }; - u16 m_class_code { 0 }; - u32 m_ttl { 0 }; - time_t m_expiration_time { 0 }; - String m_record_data; -}; diff --git a/Services/LookupServer/DNSPacket.h b/Services/LookupServer/DNSPacket.h deleted file mode 100644 index 1cddc372c7..0000000000 --- a/Services/LookupServer/DNSPacket.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Endian.h> -#include <AK/Types.h> - -class [[gnu::packed]] DNSPacket { -public: - DNSPacket() - : m_recursion_desired(false) - , m_truncated(false) - , m_authoritative_answer(false) - , m_opcode(0) - , m_query_or_response(false) - , m_response_code(0) - , m_checking_disabled(false) - , m_authenticated_data(false) - , m_zero(false) - , m_recursion_available(false) - { - } - - u16 id() const { return m_id; } - void set_id(u16 w) { m_id = w; } - - bool recursion_desired() const { return m_recursion_desired; } - void set_recursion_desired(bool b) { m_recursion_desired = b; } - - bool is_truncated() const { return m_truncated; } - void set_truncated(bool b) { m_truncated = b; } - - bool is_authoritative_answer() const { return m_authoritative_answer; } - void set_authoritative_answer(bool b) { m_authoritative_answer = b; } - - u8 opcode() const { return m_opcode; } - void set_opcode(u8 b) { m_opcode = b; } - - bool is_query() const { return !m_query_or_response; } - bool is_response() const { return m_query_or_response; } - void set_is_query() { m_query_or_response = false; } - void set_is_response() { m_query_or_response = true; } - - u8 response_code() const { return m_response_code; } - void set_response_code(u8 b) { m_response_code = b; } - - bool checking_disabled() const { return m_checking_disabled; } - void set_checking_disabled(bool b) { m_checking_disabled = b; } - - bool is_authenticated_data() const { return m_authenticated_data; } - void set_authenticated_data(bool b) { m_authenticated_data = b; } - - bool is_recursion_available() const { return m_recursion_available; } - void set_recursion_available(bool b) { m_recursion_available = b; } - - u16 question_count() const { return m_question_count; } - void set_question_count(u16 w) { m_question_count = w; } - - u16 answer_count() const { return m_answer_count; } - void set_answer_count(u16 w) { m_answer_count = w; } - - u16 authority_count() const { return m_authority_count; } - void set_authority_count(u16 w) { m_authority_count = w; } - - u16 additional_count() const { return m_additional_count; } - void set_additional_count(u16 w) { m_additional_count = w; } - - void* payload() { return this + 1; } - const void* payload() const { return this + 1; } - -private: - NetworkOrdered<u16> m_id; - - bool m_recursion_desired : 1; - bool m_truncated : 1; - bool m_authoritative_answer : 1; - u8 m_opcode : 4; - bool m_query_or_response : 1; - u8 m_response_code : 4; - bool m_checking_disabled : 1; - bool m_authenticated_data : 1; - bool m_zero : 1; - bool m_recursion_available : 1; - - NetworkOrdered<u16> m_question_count; - NetworkOrdered<u16> m_answer_count; - NetworkOrdered<u16> m_authority_count; - NetworkOrdered<u16> m_additional_count; -}; - -static_assert(sizeof(DNSPacket) == 12); diff --git a/Services/LookupServer/DNSQuestion.h b/Services/LookupServer/DNSQuestion.h deleted file mode 100644 index ec696905e6..0000000000 --- a/Services/LookupServer/DNSQuestion.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/String.h> -#include <AK/Types.h> - -class DNSQuestion { -public: - DNSQuestion(const String& name, u16 record_type, u16 class_code) - : m_name(name) - , m_record_type(record_type) - , m_class_code(class_code) - { - } - - u16 record_type() const { return m_record_type; } - u16 class_code() const { return m_class_code; } - const String& name() const { return m_name; } - - bool operator==(const DNSQuestion& other) const - { - return m_name == other.m_name && m_record_type == other.m_record_type && m_class_code == other.m_class_code; - } - - bool operator!=(const DNSQuestion& other) const - { - return !(*this == other); - } - -private: - String m_name; - u16 m_record_type { 0 }; - u16 m_class_code { 0 }; -}; diff --git a/Services/LookupServer/DNSRequest.cpp b/Services/LookupServer/DNSRequest.cpp deleted file mode 100644 index 561e988ac9..0000000000 --- a/Services/LookupServer/DNSRequest.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "DNSRequest.h" -#include "DNSPacket.h" -#include <AK/MemoryStream.h> -#include <AK/StringBuilder.h> -#include <arpa/inet.h> -#include <ctype.h> -#include <stdlib.h> - -const u16 C_IN = 1; - -DNSRequest::DNSRequest() - : m_id(arc4random_uniform(UINT16_MAX)) -{ -} - -void DNSRequest::add_question(const String& name, u16 record_type, ShouldRandomizeCase should_randomize_case) -{ - ASSERT(m_questions.size() <= UINT16_MAX); - - if (name.is_empty()) - return; - - StringBuilder builder; - for (size_t i = 0; i < name.length(); ++i) { - u8 ch = name[i]; - if (should_randomize_case == ShouldRandomizeCase::Yes) { - // Randomize the 0x20 bit in every ASCII character. - if (isalpha(ch)) { - if (arc4random_uniform(2)) - ch |= 0x20; - else - ch &= ~0x20; - } - } - builder.append(ch); - } - - if (name[name.length() - 1] != '.') - builder.append('.'); - - m_questions.empend(builder.to_string(), record_type, C_IN); -} - -ByteBuffer DNSRequest::to_byte_buffer() const -{ - DNSPacket request_header; - request_header.set_id(m_id); - request_header.set_is_query(); - request_header.set_opcode(0); - request_header.set_truncated(false); - request_header.set_recursion_desired(true); - request_header.set_question_count(m_questions.size()); - - DuplexMemoryStream stream; - - stream << ReadonlyBytes { &request_header, sizeof(request_header) }; - - for (auto& question : m_questions) { - auto parts = question.name().split('.'); - for (auto& part : parts) { - stream << (u8)part.length(); - stream << part.bytes(); - } - stream << '\0'; - stream << htons(question.record_type()); - stream << htons(question.class_code()); - } - - return stream.copy_into_contiguous_buffer(); -} diff --git a/Services/LookupServer/DNSRequest.h b/Services/LookupServer/DNSRequest.h deleted file mode 100644 index 2b55dd75fb..0000000000 --- a/Services/LookupServer/DNSRequest.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "DNSQuestion.h" -#include <AK/Types.h> -#include <AK/Vector.h> - -#define T_A 1 -#define T_NS 2 -#define T_CNAME 5 -#define T_SOA 6 -#define T_PTR 12 -#define T_MX 15 - -enum class ShouldRandomizeCase { - No = 0, - Yes -}; - -class DNSRequest { -public: - DNSRequest(); - - void add_question(const String& name, u16 record_type, ShouldRandomizeCase); - - const Vector<DNSQuestion>& questions() const { return m_questions; } - - u16 question_count() const - { - ASSERT(m_questions.size() < UINT16_MAX); - return m_questions.size(); - } - - u16 id() const { return m_id; } - ByteBuffer to_byte_buffer() const; - -private: - u16 m_id { 0 }; - Vector<DNSQuestion> m_questions; -}; diff --git a/Services/LookupServer/DNSResponse.cpp b/Services/LookupServer/DNSResponse.cpp deleted file mode 100644 index 54f4a985e8..0000000000 --- a/Services/LookupServer/DNSResponse.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "DNSResponse.h" -#include "DNSPacket.h" -#include "DNSRequest.h" -#include <AK/IPv4Address.h> -#include <AK/StringBuilder.h> - -static String parse_dns_name(const u8* data, size_t& offset, size_t max_offset, size_t recursion_level = 0); - -class [[gnu::packed]] DNSRecordWithoutName { -public: - DNSRecordWithoutName() { } - - u16 type() const { return m_type; } - u16 record_class() const { return m_class; } - u32 ttl() const { return m_ttl; } - u16 data_length() const { return m_data_length; } - - void* data() { return this + 1; } - const void* data() const { return this + 1; } - -private: - NetworkOrdered<u16> m_type; - NetworkOrdered<u16> m_class; - NetworkOrdered<u32> m_ttl; - NetworkOrdered<u16> m_data_length; -}; - -static_assert(sizeof(DNSRecordWithoutName) == 10); - -Optional<DNSResponse> DNSResponse::from_raw_response(const u8* raw_data, size_t raw_size) -{ - if (raw_size < sizeof(DNSPacket)) { - dbgln("DNS response not large enough ({} out of {}) to be a DNS packet.", raw_size, sizeof(DNSPacket)); - return {}; - } - - auto& response_header = *(const DNSPacket*)(raw_data); -#ifdef LOOKUPSERVER_DEBUG - dbgln("Got response (ID: {})", response_header.id()); - dbgln(" Question count: {}", response_header.question_count()); - dbgln(" Answer count: {}", response_header.answer_count()); - dbgln(" Authority count: {}", response_header.authority_count()); - dbgln("Additional count: {}", response_header.additional_count()); -#endif - - DNSResponse response; - response.m_id = response_header.id(); - response.m_code = response_header.response_code(); - - if (response.code() != DNSResponse::Code::NOERROR) - return response; - - size_t offset = sizeof(DNSPacket); - - for (u16 i = 0; i < response_header.question_count(); ++i) { - auto name = parse_dns_name(raw_data, offset, raw_size); - struct RawDNSAnswerQuestion { - NetworkOrdered<u16> record_type; - NetworkOrdered<u16> class_code; - }; - auto& record_and_class = *(const RawDNSAnswerQuestion*)&raw_data[offset]; - response.m_questions.empend(name, record_and_class.record_type, record_and_class.class_code); - offset += 4; -#ifdef LOOKUPSERVER_DEBUG - auto& question = response.m_questions.last(); - dbgln("Question #{}: name=_{}_, type={}, class={}", i, question.name(), question.record_type(), question.class_code()); -#endif - } - - for (u16 i = 0; i < response_header.answer_count(); ++i) { - auto name = parse_dns_name(raw_data, offset, raw_size); - - auto& record = *(const DNSRecordWithoutName*)(&raw_data[offset]); - - String data; - - offset += sizeof(DNSRecordWithoutName); - if (record.type() == T_PTR) { - size_t dummy_offset = offset; - data = parse_dns_name(raw_data, dummy_offset, raw_size); - } else if (record.type() == T_A) { - auto ipv4_address = IPv4Address((const u8*)record.data()); - data = ipv4_address.to_string(); - } else { - // FIXME: Parse some other record types perhaps? - dbgln("data=(unimplemented record type {})", record.type()); - } -#ifdef LOOKUPSERVER_DEBUG - dbgln("Answer #{}: name=_{}_, type={}, ttl={}, length={}, data=_{}_", i, name, record.type(), record.ttl(), record.data_length(), data); -#endif - response.m_answers.empend(name, record.type(), record.record_class(), record.ttl(), data); - offset += record.data_length(); - } - - return response; -} - -String parse_dns_name(const u8* data, size_t& offset, size_t max_offset, size_t recursion_level) -{ - if (recursion_level > 4) - return {}; - Vector<char, 128> buf; - while (offset < max_offset) { - u8 ch = data[offset]; - if (ch == '\0') { - ++offset; - break; - } - if ((ch & 0xc0) == 0xc0) { - if ((offset + 1) >= max_offset) - return {}; - size_t dummy = (ch & 0x3f) << 8 | data[offset + 1]; - offset += 2; - StringBuilder builder; - builder.append(buf.data(), buf.size()); - auto okay = parse_dns_name(data, dummy, max_offset, recursion_level + 1); - builder.append(okay); - return builder.to_string(); - } - for (size_t i = 0; i < ch; ++i) - buf.append(data[offset + i + 1]); - buf.append('.'); - offset += ch + 1; - } - return String::copy(buf); -} diff --git a/Services/LookupServer/DNSResponse.h b/Services/LookupServer/DNSResponse.h deleted file mode 100644 index 2bfe17fecd..0000000000 --- a/Services/LookupServer/DNSResponse.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "DNSAnswer.h" -#include "DNSQuestion.h" -#include <AK/Optional.h> -#include <AK/Types.h> -#include <AK/Vector.h> - -class DNSResponse { -public: - static Optional<DNSResponse> from_raw_response(const u8*, size_t); - - u16 id() const { return m_id; } - const Vector<DNSQuestion>& questions() const { return m_questions; } - const Vector<DNSAnswer>& answers() const { return m_answers; } - - u16 question_count() const - { - ASSERT(m_questions.size() <= UINT16_MAX); - return m_questions.size(); - } - - u16 answer_count() const - { - ASSERT(m_answers.size() <= UINT16_MAX); - return m_answers.size(); - } - - enum class Code : u8 { - NOERROR = 0, - FORMERR = 1, - SERVFAIL = 2, - NXDOMAIN = 3, - NOTIMP = 4, - REFUSED = 5, - YXDOMAIN = 6, - XRRSET = 7, - NOTAUTH = 8, - NOTZONE = 9, - }; - - Code code() const { return (Code)m_code; } - -private: - DNSResponse() { } - - u16 m_id { 0 }; - u8 m_code { 0 }; - Vector<DNSQuestion> m_questions; - Vector<DNSAnswer> m_answers; -}; diff --git a/Services/LookupServer/LookupServer.cpp b/Services/LookupServer/LookupServer.cpp deleted file mode 100644 index 16761ec7e4..0000000000 --- a/Services/LookupServer/LookupServer.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "LookupServer.h" -#include "DNSRequest.h" -#include "DNSResponse.h" -#include <AK/ByteBuffer.h> -#include <AK/HashMap.h> -#include <AK/String.h> -#include <AK/StringBuilder.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/File.h> -#include <LibCore/LocalServer.h> -#include <LibCore/LocalSocket.h> -#include <LibCore/UDPSocket.h> -#include <stdio.h> -#include <sys/time.h> -#include <unistd.h> - -//#define LOOKUPSERVER_DEBUG - -LookupServer::LookupServer() -{ - auto config = Core::ConfigFile::get_for_system("LookupServer"); - dbgln("Using network config file at {}", config->file_name()); - m_nameservers = config->read_entry("DNS", "Nameservers", "1.1.1.1,1.0.0.1").split(','); - - load_etc_hosts(); - - m_local_server = Core::LocalServer::construct(this); - m_local_server->on_ready_to_accept = [this]() { - auto socket = m_local_server->accept(); - socket->on_ready_to_read = [this, socket]() { - service_client(socket); - RefPtr<Core::LocalSocket> keeper = socket; - const_cast<Core::LocalSocket&>(*socket).on_ready_to_read = [] {}; - }; - }; - bool ok = m_local_server->take_over_from_system_server(); - ASSERT(ok); -} - -void LookupServer::load_etc_hosts() -{ - auto file = Core::File::construct("/etc/hosts"); - if (!file->open(Core::IODevice::ReadOnly)) - return; - while (!file->eof()) { - auto line = file->read_line(1024); - if (line.is_empty()) - break; - auto fields = line.split('\t'); - - auto sections = fields[0].split('.'); - IPv4Address addr { - (u8)atoi(sections[0].characters()), - (u8)atoi(sections[1].characters()), - (u8)atoi(sections[2].characters()), - (u8)atoi(sections[3].characters()), - }; - - auto name = fields[1]; - m_etc_hosts.set(name, addr.to_string()); - - IPv4Address reverse_addr { - (u8)atoi(sections[3].characters()), - (u8)atoi(sections[2].characters()), - (u8)atoi(sections[1].characters()), - (u8)atoi(sections[0].characters()), - }; - StringBuilder builder; - builder.append(reverse_addr.to_string()); - builder.append(".in-addr.arpa"); - m_etc_hosts.set(builder.to_string(), name); - } -} - -void LookupServer::service_client(RefPtr<Core::LocalSocket> socket) -{ - u8 client_buffer[1024]; - int nrecv = socket->read(client_buffer, sizeof(client_buffer) - 1); - if (nrecv < 0) { - perror("read"); - return; - } - - client_buffer[nrecv] = '\0'; - - char lookup_type = client_buffer[0]; - if (lookup_type != 'L' && lookup_type != 'R') { - dbgln("Invalid lookup_type '{}'", lookup_type); - return; - } - auto hostname = String((const char*)client_buffer + 1, nrecv - 1, Chomp); -#ifdef LOOKUPSERVER_DEBUG - dbgln("Got request for '{}'", hostname); -#endif - - Vector<String> responses; - - if (auto known_host = m_etc_hosts.get(hostname); known_host.has_value()) { - responses.append(known_host.value()); - } else if (!hostname.is_empty()) { - for (auto& nameserver : m_nameservers) { -#ifdef LOOKUPSERVER_DEBUG - dbgln("Doing lookup using nameserver '{}'", nameserver); -#endif - bool did_get_response = false; - int retries = 3; - do { - if (lookup_type == 'L') - responses = lookup(hostname, nameserver, did_get_response, T_A); - else if (lookup_type == 'R') - responses = lookup(hostname, nameserver, did_get_response, T_PTR); - if (did_get_response) - break; - } while (--retries); - if (!responses.is_empty()) { - break; - } else { - if (!did_get_response) - dbgln("Never got a response from '{}', trying next nameserver", nameserver); - else - dbgln("Received response from '{}' but no result(s), trying next nameserver", nameserver); - } - } - if (responses.is_empty()) { - fprintf(stderr, "LookupServer: Tried all nameservers but never got a response :(\n"); - return; - } - } - - if (responses.is_empty()) { - int nsent = socket->write("Not found.\n"); - if (nsent < 0) - perror("write"); - return; - } - for (auto& response : responses) { - auto line = String::format("%s\n", response.characters()); - int nsent = socket->write(line); - if (nsent < 0) { - perror("write"); - break; - } - } -} - -Vector<String> LookupServer::lookup(const String& hostname, const String& nameserver, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase should_randomize_case) -{ - if (auto it = m_lookup_cache.find(hostname); it != m_lookup_cache.end()) { - auto& cached_lookup = it->value; - if (cached_lookup.question.record_type() == record_type) { - Vector<String> responses; - for (auto& cached_answer : cached_lookup.answers) { -#ifdef LOOKUPSERVER_DEBUG - dbgln("Cache hit: {} -> {}, expired: {}", hostname, cached_answer.record_data(), cached_answer.has_expired()); -#endif - if (!cached_answer.has_expired()) - responses.append(cached_answer.record_data()); - } - if (!responses.is_empty()) - return responses; - } - m_lookup_cache.remove(it); - } - - DNSRequest request; - request.add_question(hostname, record_type, should_randomize_case); - - auto buffer = request.to_byte_buffer(); - - auto udp_socket = Core::UDPSocket::construct(); - udp_socket->set_blocking(true); - - struct timeval timeout { - 1, 0 - }; - - int rc = setsockopt(udp_socket->fd(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); - if (rc < 0) { - perror("setsockopt(SOL_SOCKET, SO_RCVTIMEO)"); - return {}; - } - - if (!udp_socket->connect(nameserver, 53)) - return {}; - - if (!udp_socket->write(buffer)) - return {}; - - u8 response_buffer[4096]; - int nrecv = udp_socket->read(response_buffer, sizeof(response_buffer)); - if (nrecv == 0) - return {}; - - did_get_response = true; - - auto o_response = DNSResponse::from_raw_response(response_buffer, nrecv); - if (!o_response.has_value()) - return {}; - - auto& response = o_response.value(); - - if (response.id() != request.id()) { - dbgln("LookupServer: ID mismatch ({} vs {}) :(", response.id(), request.id()); - return {}; - } - - if (response.code() == DNSResponse::Code::REFUSED) { - if (should_randomize_case == ShouldRandomizeCase::Yes) { - // Retry with 0x20 case randomization turned off. - return lookup(hostname, nameserver, did_get_response, record_type, ShouldRandomizeCase::No); - } - return {}; - } - - if (response.question_count() != request.question_count()) { - dbgln("LookupServer: Question count ({} vs {}) :(", response.question_count(), request.question_count()); - return {}; - } - - for (size_t i = 0; i < request.question_count(); ++i) { - auto& request_question = request.questions()[i]; - auto& response_question = response.questions()[i]; - if (request_question != response_question) { - dbgln("Request and response questions do not match"); - dbgln(" Request: name=_{}_, type={}, class={}", request_question.name(), response_question.record_type(), response_question.class_code()); - dbgln(" Response: name=_{}_, type={}, class={}", response_question.name(), response_question.record_type(), response_question.class_code()); - return {}; - } - } - - if (response.answer_count() < 1) { - dbgln("LookupServer: Not enough answers ({}) :(", response.answer_count()); - return {}; - } - - Vector<String, 8> responses; - Vector<DNSAnswer, 8> cacheable_answers; - for (auto& answer : response.answers()) { - if (answer.type() != T_A) - continue; - responses.append(answer.record_data()); - if (!answer.has_expired()) - cacheable_answers.append(answer); - } - - if (!cacheable_answers.is_empty()) { - if (m_lookup_cache.size() >= 256) - m_lookup_cache.remove(m_lookup_cache.begin()); - m_lookup_cache.set(hostname, { request.questions()[0], move(cacheable_answers) }); - } - return responses; -} diff --git a/Services/LookupServer/LookupServer.h b/Services/LookupServer/LookupServer.h deleted file mode 100644 index 7b5a08140a..0000000000 --- a/Services/LookupServer/LookupServer.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "DNSRequest.h" -#include "DNSResponse.h" -#include <AK/HashMap.h> -#include <LibCore/Object.h> - -class DNSAnswer; - -class LookupServer final : public Core::Object { - C_OBJECT(LookupServer) - -public: - LookupServer(); - -private: - void load_etc_hosts(); - void service_client(RefPtr<Core::LocalSocket>); - Vector<String> lookup(const String& hostname, const String& nameserver, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase = ShouldRandomizeCase::Yes); - - struct CachedLookup { - DNSQuestion question; - Vector<DNSAnswer> answers; - }; - - RefPtr<Core::LocalServer> m_local_server; - Vector<String> m_nameservers; - HashMap<String, String> m_etc_hosts; - HashMap<String, CachedLookup> m_lookup_cache; -}; diff --git a/Services/LookupServer/main.cpp b/Services/LookupServer/main.cpp deleted file mode 100644 index 23d5933bac..0000000000 --- a/Services/LookupServer/main.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "LookupServer.h" -#include <LibCore/EventLoop.h> -#include <LibCore/LocalServer.h> -#include <stdio.h> - -int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) -{ - if (pledge("stdio accept unix inet cpath rpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - Core::EventLoop event_loop; - LookupServer server; - - if (pledge("stdio accept inet", nullptr) < 0) { - perror("pledge"); - return 1; - } - - unveil(nullptr, nullptr); - - return event_loop.exec(); -} diff --git a/Services/NotificationServer/CMakeLists.txt b/Services/NotificationServer/CMakeLists.txt deleted file mode 100644 index a28d9f16a5..0000000000 --- a/Services/NotificationServer/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -compile_ipc(NotificationServer.ipc NotificationServerEndpoint.h) -compile_ipc(NotificationClient.ipc NotificationClientEndpoint.h) - -set(SOURCES - ClientConnection.cpp - main.cpp - NotificationWindow.cpp - NotificationServerEndpoint.h - NotificationClientEndpoint.h -) - -serenity_bin(NotificationServer) -target_link_libraries(NotificationServer LibGUI LibIPC) diff --git a/Services/NotificationServer/ClientConnection.cpp b/Services/NotificationServer/ClientConnection.cpp deleted file mode 100644 index 9c6cc8051a..0000000000 --- a/Services/NotificationServer/ClientConnection.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ClientConnection.h" -#include "NotificationWindow.h" -#include <AK/HashMap.h> -#include <NotificationServer/NotificationClientEndpoint.h> - -namespace NotificationServer { - -static HashMap<int, RefPtr<ClientConnection>> s_connections; - -ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id) - : IPC::ClientConnection<NotificationClientEndpoint, NotificationServerEndpoint>(*this, move(client_socket), client_id) -{ - s_connections.set(client_id, *this); -} - -ClientConnection::~ClientConnection() -{ -} - -void ClientConnection::die() -{ - s_connections.remove(client_id()); -} - -OwnPtr<Messages::NotificationServer::GreetResponse> ClientConnection::handle(const Messages::NotificationServer::Greet&) -{ - return make<Messages::NotificationServer::GreetResponse>(client_id()); -} - -OwnPtr<Messages::NotificationServer::ShowNotificationResponse> ClientConnection::handle(const Messages::NotificationServer::ShowNotification& message) -{ - auto window = NotificationWindow::construct(message.text(), message.title(), message.icon()); - window->show(); - return make<Messages::NotificationServer::ShowNotificationResponse>(); -} - -} diff --git a/Services/NotificationServer/ClientConnection.h b/Services/NotificationServer/ClientConnection.h deleted file mode 100644 index af4e36d0aa..0000000000 --- a/Services/NotificationServer/ClientConnection.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <LibIPC/ClientConnection.h> -#include <NotificationServer/NotificationClientEndpoint.h> -#include <NotificationServer/NotificationServerEndpoint.h> - -namespace NotificationServer { - -class ClientConnection final : public IPC::ClientConnection<NotificationClientEndpoint, NotificationServerEndpoint> - , public NotificationServerEndpoint { - C_OBJECT(ClientConnection) -public: - ~ClientConnection() override; - - virtual void die() override; - -private: - explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); - - virtual OwnPtr<Messages::NotificationServer::GreetResponse> handle(const Messages::NotificationServer::Greet&) override; - virtual OwnPtr<Messages::NotificationServer::ShowNotificationResponse> handle(const Messages::NotificationServer::ShowNotification&) override; -}; - -} diff --git a/Services/NotificationServer/NotificationClient.ipc b/Services/NotificationServer/NotificationClient.ipc deleted file mode 100644 index fc85168f76..0000000000 --- a/Services/NotificationServer/NotificationClient.ipc +++ /dev/null @@ -1,4 +0,0 @@ -endpoint NotificationClient = 92 -{ - Dummy() =| -} diff --git a/Services/NotificationServer/NotificationServer.ipc b/Services/NotificationServer/NotificationServer.ipc deleted file mode 100644 index e342ebf2d3..0000000000 --- a/Services/NotificationServer/NotificationServer.ipc +++ /dev/null @@ -1,7 +0,0 @@ -endpoint NotificationServer = 95 -{ - // Basic protocol - Greet() => (i32 client_id) - - ShowNotification([UTF8] String text, [UTF8] String title, Gfx::ShareableBitmap icon) => () -} diff --git a/Services/NotificationServer/NotificationWindow.cpp b/Services/NotificationServer/NotificationWindow.cpp deleted file mode 100644 index e5739b1c3b..0000000000 --- a/Services/NotificationServer/NotificationWindow.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "NotificationWindow.h" -#include <AK/Vector.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Desktop.h> -#include <LibGUI/ImageWidget.h> -#include <LibGUI/Label.h> -#include <LibGUI/Widget.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> -#include <LibGfx/ShareableBitmap.h> - -namespace NotificationServer { - -static Vector<RefPtr<NotificationWindow>> s_windows; - -void update_notification_window_locations() -{ - Gfx::IntRect last_window_rect; - for (auto& window : s_windows) { - Gfx::IntPoint new_window_location; - if (last_window_rect.is_null()) - new_window_location = GUI::Desktop::the().rect().top_right().translated(-window->rect().width() - 24, 26); - else - new_window_location = last_window_rect.bottom_left().translated(0, 10); - if (window->rect().location() != new_window_location) { - window->move_to(new_window_location); - window->set_original_rect(window->rect()); - } - last_window_rect = window->rect(); - } -} - -NotificationWindow::NotificationWindow(const String& text, const String& title, const Gfx::ShareableBitmap& icon) -{ - s_windows.append(this); - - set_window_type(GUI::WindowType::Notification); - set_resizable(false); - set_minimizable(false); - - Gfx::IntRect lowest_notification_rect_on_screen; - for (auto& window : s_windows) { - if (window->m_original_rect.y() > lowest_notification_rect_on_screen.y()) - lowest_notification_rect_on_screen = window->m_original_rect; - } - - Gfx::IntRect rect; - rect.set_width(220); - rect.set_height(40); - rect.set_location(GUI::Desktop::the().rect().top_right().translated(-rect.width() - 24, 26)); - - if (!lowest_notification_rect_on_screen.is_null()) - rect.set_location(lowest_notification_rect_on_screen.bottom_left().translated(0, 10)); - - set_rect(rect); - - m_original_rect = rect; - - auto& widget = set_main_widget<GUI::Widget>(); - widget.set_fill_with_background_color(true); - - widget.set_layout<GUI::HorizontalBoxLayout>(); - widget.layout()->set_margins({ 8, 8, 8, 8 }); - widget.layout()->set_spacing(6); - - if (icon.is_valid()) { - auto& image = widget.add<GUI::ImageWidget>(); - image.set_bitmap(icon.bitmap()); - } - - auto& left_container = widget.add<GUI::Widget>(); - left_container.set_layout<GUI::VerticalBoxLayout>(); - - auto& title_label = left_container.add<GUI::Label>(title); - title_label.set_font(Gfx::FontDatabase::default_bold_font()); - title_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - auto& text_label = left_container.add<GUI::Label>(text); - text_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); - - auto& right_container = widget.add<GUI::Widget>(); - right_container.set_fixed_width(36); - right_container.set_layout<GUI::HorizontalBoxLayout>(); - - on_close_request = [this] { - s_windows.remove_first_matching([this](auto& entry) { return entry == this; }); - update_notification_window_locations(); - return CloseRequestDecision::Close; - }; -} - -NotificationWindow::~NotificationWindow() -{ -} - -} diff --git a/Services/NotificationServer/NotificationWindow.h b/Services/NotificationServer/NotificationWindow.h deleted file mode 100644 index 607365fe6e..0000000000 --- a/Services/NotificationServer/NotificationWindow.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <LibGUI/Window.h> - -namespace NotificationServer { - -void update_notification_window_locations(); - -class NotificationWindow final : public GUI::Window { - C_OBJECT(NotificationWindow); - -public: - virtual ~NotificationWindow() override; - void set_original_rect(Gfx::IntRect original_rect) { m_original_rect = original_rect; }; - -private: - NotificationWindow(const String& text, const String& title, const Gfx::ShareableBitmap&); - - Gfx::IntRect m_original_rect; -}; - -} diff --git a/Services/NotificationServer/main.cpp b/Services/NotificationServer/main.cpp deleted file mode 100644 index 91cd06e0d7..0000000000 --- a/Services/NotificationServer/main.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ClientConnection.h" -#include "NotificationWindow.h" -#include <LibCore/LocalServer.h> -#include <LibGUI/Application.h> -#include <LibGUI/Desktop.h> -#include <LibGUI/WindowServerConnection.h> -#include <stdio.h> -#include <unistd.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer accept rpath wpath cpath unix fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - auto server = Core::LocalServer::construct(); - - bool ok = server->take_over_from_system_server(); - ASSERT(ok); - server->on_ready_to_accept = [&] { - auto client_socket = server->accept(); - if (!client_socket) { - dbgln("NotificationServer: accept failed."); - return; - } - static int s_next_client_id = 0; - int client_id = ++s_next_client_id; - IPC::new_client_connection<NotificationServer::ClientConnection>(client_socket.release_nonnull(), client_id); - }; - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - if (pledge("stdio shared_buffer accept rpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - GUI::Desktop::the().on_rect_change = [](auto&) { NotificationServer::update_notification_window_locations(); }; - - return app->exec(); -} diff --git a/Services/ProtocolServer/CMakeLists.txt b/Services/ProtocolServer/CMakeLists.txt deleted file mode 100644 index 4fb0757759..0000000000 --- a/Services/ProtocolServer/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -compile_ipc(ProtocolServer.ipc ProtocolServerEndpoint.h) -compile_ipc(ProtocolClient.ipc ProtocolClientEndpoint.h) - -set(SOURCES - ClientConnection.cpp - Download.cpp - GeminiDownload.cpp - GeminiProtocol.cpp - HttpDownload.cpp - HttpProtocol.cpp - HttpsDownload.cpp - HttpsProtocol.cpp - main.cpp - Protocol.cpp - ProtocolServerEndpoint.h - ProtocolClientEndpoint.h -) - -serenity_bin(ProtocolServer) -target_link_libraries(ProtocolServer LibCore LibIPC LibGemini LibHTTP) diff --git a/Services/ProtocolServer/ClientConnection.cpp b/Services/ProtocolServer/ClientConnection.cpp deleted file mode 100644 index e966d2f2f4..0000000000 --- a/Services/ProtocolServer/ClientConnection.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Badge.h> -#include <AK/SharedBuffer.h> -#include <ProtocolServer/ClientConnection.h> -#include <ProtocolServer/Download.h> -#include <ProtocolServer/Protocol.h> -#include <ProtocolServer/ProtocolClientEndpoint.h> - -namespace ProtocolServer { - -static HashMap<int, RefPtr<ClientConnection>> s_connections; - -ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id) - : IPC::ClientConnection<ProtocolClientEndpoint, ProtocolServerEndpoint>(*this, move(socket), client_id) -{ - s_connections.set(client_id, *this); -} - -ClientConnection::~ClientConnection() -{ -} - -void ClientConnection::die() -{ - s_connections.remove(client_id()); - if (s_connections.is_empty()) - Core::EventLoop::current().quit(0); -} - -OwnPtr<Messages::ProtocolServer::IsSupportedProtocolResponse> ClientConnection::handle(const Messages::ProtocolServer::IsSupportedProtocol& message) -{ - bool supported = Protocol::find_by_name(message.protocol().to_lowercase()); - return make<Messages::ProtocolServer::IsSupportedProtocolResponse>(supported); -} - -OwnPtr<Messages::ProtocolServer::StartDownloadResponse> ClientConnection::handle(const Messages::ProtocolServer::StartDownload& message) -{ - const auto& url = message.url(); - if (!url.is_valid()) { - dbgln("StartDownload: Invalid URL requested: '{}'", url); - return make<Messages::ProtocolServer::StartDownloadResponse>(-1, Optional<IPC::File> {}); - } - auto* protocol = Protocol::find_by_name(url.protocol()); - if (!protocol) { - dbgln("StartDownload: No protocol handler for URL: '{}'", url); - return make<Messages::ProtocolServer::StartDownloadResponse>(-1, Optional<IPC::File> {}); - } - auto download = protocol->start_download(*this, message.method(), url, message.request_headers().entries(), message.request_body()); - if (!download) { - dbgln("StartDownload: Protocol handler failed to start download: '{}'", url); - return make<Messages::ProtocolServer::StartDownloadResponse>(-1, Optional<IPC::File> {}); - } - auto id = download->id(); - auto fd = download->download_fd(); - m_downloads.set(id, move(download)); - auto response = make<Messages::ProtocolServer::StartDownloadResponse>(id, fd); - response->on_destruction = [fd] { close(fd); }; - return response; -} - -OwnPtr<Messages::ProtocolServer::StopDownloadResponse> ClientConnection::handle(const Messages::ProtocolServer::StopDownload& message) -{ - auto* download = const_cast<Download*>(m_downloads.get(message.download_id()).value_or(nullptr)); - bool success = false; - if (download) { - download->stop(); - m_downloads.remove(message.download_id()); - success = true; - } - return make<Messages::ProtocolServer::StopDownloadResponse>(success); -} - -void ClientConnection::did_receive_headers(Badge<Download>, Download& download) -{ - IPC::Dictionary response_headers; - for (auto& it : download.response_headers()) - response_headers.add(it.key, it.value); - - post_message(Messages::ProtocolClient::HeadersBecameAvailable(download.id(), move(response_headers), download.status_code())); -} - -void ClientConnection::did_finish_download(Badge<Download>, Download& download, bool success) -{ - ASSERT(download.total_size().has_value()); - - post_message(Messages::ProtocolClient::DownloadFinished(download.id(), success, download.total_size().value())); - - m_downloads.remove(download.id()); -} - -void ClientConnection::did_progress_download(Badge<Download>, Download& download) -{ - post_message(Messages::ProtocolClient::DownloadProgress(download.id(), download.total_size(), download.downloaded_size())); -} - -void ClientConnection::did_request_certificates(Badge<Download>, Download& download) -{ - post_message(Messages::ProtocolClient::CertificateRequested(download.id())); -} - -OwnPtr<Messages::ProtocolServer::GreetResponse> ClientConnection::handle(const Messages::ProtocolServer::Greet&) -{ - return make<Messages::ProtocolServer::GreetResponse>(client_id()); -} - -OwnPtr<Messages::ProtocolServer::SetCertificateResponse> ClientConnection::handle(const Messages::ProtocolServer::SetCertificate& message) -{ - auto* download = const_cast<Download*>(m_downloads.get(message.download_id()).value_or(nullptr)); - bool success = false; - if (download) { - download->set_certificate(message.certificate(), message.key()); - success = true; - } - return make<Messages::ProtocolServer::SetCertificateResponse>(success); -} - -} diff --git a/Services/ProtocolServer/ClientConnection.h b/Services/ProtocolServer/ClientConnection.h deleted file mode 100644 index 778f3eff81..0000000000 --- a/Services/ProtocolServer/ClientConnection.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/HashMap.h> -#include <LibIPC/ClientConnection.h> -#include <ProtocolServer/Forward.h> -#include <ProtocolServer/ProtocolClientEndpoint.h> -#include <ProtocolServer/ProtocolServerEndpoint.h> - -namespace ProtocolServer { - -class ClientConnection final - : public IPC::ClientConnection<ProtocolClientEndpoint, ProtocolServerEndpoint> - , public ProtocolServerEndpoint { - C_OBJECT(ClientConnection); - -public: - explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); - ~ClientConnection() override; - - virtual void die() override; - - void did_receive_headers(Badge<Download>, Download&); - void did_finish_download(Badge<Download>, Download&, bool success); - void did_progress_download(Badge<Download>, Download&); - void did_request_certificates(Badge<Download>, Download&); - -private: - virtual OwnPtr<Messages::ProtocolServer::GreetResponse> handle(const Messages::ProtocolServer::Greet&) override; - virtual OwnPtr<Messages::ProtocolServer::IsSupportedProtocolResponse> handle(const Messages::ProtocolServer::IsSupportedProtocol&) override; - virtual OwnPtr<Messages::ProtocolServer::StartDownloadResponse> handle(const Messages::ProtocolServer::StartDownload&) override; - virtual OwnPtr<Messages::ProtocolServer::StopDownloadResponse> handle(const Messages::ProtocolServer::StopDownload&) override; - virtual OwnPtr<Messages::ProtocolServer::SetCertificateResponse> handle(const Messages::ProtocolServer::SetCertificate&) override; - - HashMap<i32, OwnPtr<Download>> m_downloads; -}; - -} diff --git a/Services/ProtocolServer/Download.cpp b/Services/ProtocolServer/Download.cpp deleted file mode 100644 index 11a7d28933..0000000000 --- a/Services/ProtocolServer/Download.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Badge.h> -#include <ProtocolServer/ClientConnection.h> -#include <ProtocolServer/Download.h> - -namespace ProtocolServer { - -// FIXME: What about rollover? -static i32 s_next_id = 1; - -Download::Download(ClientConnection& client, NonnullOwnPtr<OutputFileStream>&& output_stream) - : m_client(client) - , m_id(s_next_id++) - , m_output_stream(move(output_stream)) -{ -} - -Download::~Download() -{ -} - -void Download::stop() -{ - m_client.did_finish_download({}, *this, false); -} - -void Download::set_response_headers(const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers) -{ - m_response_headers = response_headers; - m_client.did_receive_headers({}, *this); -} - -void Download::set_certificate(String, String) -{ -} - -void Download::did_finish(bool success) -{ - m_client.did_finish_download({}, *this, success); -} - -void Download::did_progress(Optional<u32> total_size, u32 downloaded_size) -{ - m_total_size = total_size; - m_downloaded_size = downloaded_size; - m_client.did_progress_download({}, *this); -} - -void Download::did_request_certificates() -{ - m_client.did_request_certificates({}, *this); -} - -} diff --git a/Services/ProtocolServer/Download.h b/Services/ProtocolServer/Download.h deleted file mode 100644 index 35c60269f4..0000000000 --- a/Services/ProtocolServer/Download.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/FileStream.h> -#include <AK/HashMap.h> -#include <AK/NonnullOwnPtr.h> -#include <AK/Optional.h> -#include <AK/RefCounted.h> -#include <AK/URL.h> -#include <ProtocolServer/Forward.h> - -namespace ProtocolServer { - -class Download { -public: - virtual ~Download(); - - i32 id() const { return m_id; } - URL url() const { return m_url; } - - Optional<u32> status_code() const { return m_status_code; } - Optional<u32> total_size() const { return m_total_size; } - size_t downloaded_size() const { return m_downloaded_size; } - const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers() const { return m_response_headers; } - - void stop(); - virtual void set_certificate(String, String); - - // FIXME: Want Badge<Protocol>, but can't make one from HttpProtocol, etc. - void set_download_fd(int fd) { m_download_fd = fd; } - int download_fd() const { return m_download_fd; } - -protected: - explicit Download(ClientConnection&, NonnullOwnPtr<OutputFileStream>&&); - - void did_finish(bool success); - void did_progress(Optional<u32> total_size, u32 downloaded_size); - void set_status_code(u32 status_code) { m_status_code = status_code; } - void did_request_certificates(); - void set_response_headers(const HashMap<String, String, CaseInsensitiveStringTraits>&); - void set_downloaded_size(size_t size) { m_downloaded_size = size; } - const OutputFileStream& output_stream() const { return *m_output_stream; } - -private: - ClientConnection& m_client; - i32 m_id { 0 }; - int m_download_fd { -1 }; // Passed to client. - URL m_url; - Optional<u32> m_status_code; - Optional<u32> m_total_size {}; - size_t m_downloaded_size { 0 }; - NonnullOwnPtr<OutputFileStream> m_output_stream; - HashMap<String, String, CaseInsensitiveStringTraits> m_response_headers; -}; - -} diff --git a/Services/ProtocolServer/Forward.h b/Services/ProtocolServer/Forward.h deleted file mode 100644 index 06eec6c7ab..0000000000 --- a/Services/ProtocolServer/Forward.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -namespace ProtocolServer { - -class ClientConnection; -class Download; -class GeminiProtocol; -class HttpDownload; -class HttpProtocol; -class HttpsDownload; -class HttpsProtocol; -class Protocol; - -} diff --git a/Services/ProtocolServer/GeminiDownload.cpp b/Services/ProtocolServer/GeminiDownload.cpp deleted file mode 100644 index 0bba75519d..0000000000 --- a/Services/ProtocolServer/GeminiDownload.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibGemini/GeminiJob.h> -#include <LibGemini/GeminiResponse.h> -#include <ProtocolServer/GeminiDownload.h> - -namespace ProtocolServer { - -GeminiDownload::GeminiDownload(ClientConnection& client, NonnullRefPtr<Gemini::GeminiJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream) - : Download(client, move(output_stream)) - , m_job(job) -{ - m_job->on_finish = [this](bool success) { - if (auto* response = m_job->response()) { - set_downloaded_size(this->output_stream().size()); - if (!response->meta().is_empty()) { - HashMap<String, String, CaseInsensitiveStringTraits> headers; - headers.set("meta", response->meta()); - // Note: We're setting content-type to meta only on status==SUCCESS - // we should perhaps have a better mechanism for this, since we - // are already shoehorning the concept of "headers" here - if (response->status() >= 20 && response->status() < 30) { - headers.set("content-type", response->meta()); - } - set_response_headers(headers); - } - } - - // signal 100% download progress so any listeners can react - // appropriately - did_progress(downloaded_size(), downloaded_size()); - - did_finish(success); - }; - m_job->on_progress = [this](Optional<u32> total, u32 current) { - did_progress(total, current); - }; - m_job->on_certificate_requested = [this](auto&) { - did_request_certificates(); - }; -} - -void GeminiDownload::set_certificate(String certificate, String key) -{ - m_job->set_certificate(move(certificate), move(key)); -} - -GeminiDownload::~GeminiDownload() -{ - m_job->on_finish = nullptr; - m_job->on_progress = nullptr; - m_job->shutdown(); -} - -NonnullOwnPtr<GeminiDownload> GeminiDownload::create_with_job(Badge<GeminiProtocol>, ClientConnection& client, NonnullRefPtr<Gemini::GeminiJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream) -{ - return adopt_own(*new GeminiDownload(client, move(job), move(output_stream))); -} - -} diff --git a/Services/ProtocolServer/GeminiDownload.h b/Services/ProtocolServer/GeminiDownload.h deleted file mode 100644 index fcd81c121f..0000000000 --- a/Services/ProtocolServer/GeminiDownload.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Badge.h> -#include <LibCore/Forward.h> -#include <LibGemini/Forward.h> -#include <ProtocolServer/Download.h> - -namespace ProtocolServer { - -class GeminiDownload final : public Download { -public: - virtual ~GeminiDownload() override; - static NonnullOwnPtr<GeminiDownload> create_with_job(Badge<GeminiProtocol>, ClientConnection&, NonnullRefPtr<Gemini::GeminiJob>, NonnullOwnPtr<OutputFileStream>&&); - -private: - explicit GeminiDownload(ClientConnection&, NonnullRefPtr<Gemini::GeminiJob>, NonnullOwnPtr<OutputFileStream>&&); - - virtual void set_certificate(String certificate, String key) override; - - NonnullRefPtr<Gemini::GeminiJob> m_job; -}; - -} diff --git a/Services/ProtocolServer/GeminiProtocol.cpp b/Services/ProtocolServer/GeminiProtocol.cpp deleted file mode 100644 index c09b53ca42..0000000000 --- a/Services/ProtocolServer/GeminiProtocol.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibGemini/GeminiJob.h> -#include <LibGemini/GeminiRequest.h> -#include <ProtocolServer/GeminiDownload.h> -#include <ProtocolServer/GeminiProtocol.h> -#include <fcntl.h> - -namespace ProtocolServer { - -GeminiProtocol::GeminiProtocol() - : Protocol("gemini") -{ -} - -GeminiProtocol::~GeminiProtocol() -{ -} - -OwnPtr<Download> GeminiProtocol::start_download(ClientConnection& client, const String&, const URL& url, const HashMap<String, String>&, ReadonlyBytes) -{ - Gemini::GeminiRequest request; - request.set_url(url); - - auto pipe_result = get_pipe_for_download(); - if (pipe_result.is_error()) - return {}; - - auto output_stream = make<OutputFileStream>(pipe_result.value().write_fd); - output_stream->make_unbuffered(); - auto job = Gemini::GeminiJob::construct(request, *output_stream); - auto download = GeminiDownload::create_with_job({}, client, (Gemini::GeminiJob&)*job, move(output_stream)); - download->set_download_fd(pipe_result.value().read_fd); - job->start(); - return download; -} - -} diff --git a/Services/ProtocolServer/GeminiProtocol.h b/Services/ProtocolServer/GeminiProtocol.h deleted file mode 100644 index 23a4d7c717..0000000000 --- a/Services/ProtocolServer/GeminiProtocol.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <ProtocolServer/Protocol.h> - -namespace ProtocolServer { - -class GeminiProtocol final : public Protocol { -public: - GeminiProtocol(); - virtual ~GeminiProtocol() override; - - virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>&, ReadonlyBytes body) override; -}; - -} diff --git a/Services/ProtocolServer/HttpDownload.cpp b/Services/ProtocolServer/HttpDownload.cpp deleted file mode 100644 index 8ba945d335..0000000000 --- a/Services/ProtocolServer/HttpDownload.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibHTTP/HttpJob.h> -#include <LibHTTP/HttpResponse.h> -#include <ProtocolServer/HttpDownload.h> - -namespace ProtocolServer { - -HttpDownload::HttpDownload(ClientConnection& client, NonnullRefPtr<HTTP::HttpJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream) - : Download(client, move(output_stream)) - , m_job(job) -{ - m_job->on_headers_received = [this](auto& headers, auto response_code) { - if (response_code.has_value()) - set_status_code(response_code.value()); - set_response_headers(headers); - }; - - m_job->on_finish = [this](bool success) { - if (auto* response = m_job->response()) { - set_status_code(response->code()); - set_response_headers(response->headers()); - set_downloaded_size(this->output_stream().size()); - } - - // if we didn't know the total size, pretend that the download finished successfully - // and set the total size to the downloaded size - if (!total_size().has_value()) - did_progress(downloaded_size(), downloaded_size()); - - did_finish(success); - }; - m_job->on_progress = [this](Optional<u32> total, u32 current) { - did_progress(total, current); - }; -} - -HttpDownload::~HttpDownload() -{ - m_job->on_finish = nullptr; - m_job->on_progress = nullptr; - m_job->shutdown(); -} - -NonnullOwnPtr<HttpDownload> HttpDownload::create_with_job(Badge<HttpProtocol>, ClientConnection& client, NonnullRefPtr<HTTP::HttpJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream) -{ - return adopt_own(*new HttpDownload(client, move(job), move(output_stream))); -} - -} diff --git a/Services/ProtocolServer/HttpDownload.h b/Services/ProtocolServer/HttpDownload.h deleted file mode 100644 index 50095bd0e5..0000000000 --- a/Services/ProtocolServer/HttpDownload.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Badge.h> -#include <LibCore/Forward.h> -#include <LibHTTP/Forward.h> -#include <ProtocolServer/Download.h> - -namespace ProtocolServer { - -class HttpDownload final : public Download { -public: - virtual ~HttpDownload() override; - static NonnullOwnPtr<HttpDownload> create_with_job(Badge<HttpProtocol>, ClientConnection&, NonnullRefPtr<HTTP::HttpJob>, NonnullOwnPtr<OutputFileStream>&&); - -private: - explicit HttpDownload(ClientConnection&, NonnullRefPtr<HTTP::HttpJob>, NonnullOwnPtr<OutputFileStream>&&); - - NonnullRefPtr<HTTP::HttpJob> m_job; -}; - -} diff --git a/Services/ProtocolServer/HttpProtocol.cpp b/Services/ProtocolServer/HttpProtocol.cpp deleted file mode 100644 index acc10a88ec..0000000000 --- a/Services/ProtocolServer/HttpProtocol.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibHTTP/HttpJob.h> -#include <LibHTTP/HttpRequest.h> -#include <ProtocolServer/HttpDownload.h> -#include <ProtocolServer/HttpProtocol.h> - -namespace ProtocolServer { - -HttpProtocol::HttpProtocol() - : Protocol("http") -{ -} - -HttpProtocol::~HttpProtocol() -{ -} - -OwnPtr<Download> HttpProtocol::start_download(ClientConnection& client, const String& method, const URL& url, const HashMap<String, String>& headers, ReadonlyBytes body) -{ - HTTP::HttpRequest request; - if (method.equals_ignoring_case("post")) - request.set_method(HTTP::HttpRequest::Method::POST); - else - request.set_method(HTTP::HttpRequest::Method::GET); - request.set_url(url); - request.set_headers(headers); - request.set_body(body); - - auto pipe_result = get_pipe_for_download(); - if (pipe_result.is_error()) - return {}; - - auto output_stream = make<OutputFileStream>(pipe_result.value().write_fd); - output_stream->make_unbuffered(); - auto job = HTTP::HttpJob::construct(request, *output_stream); - auto download = HttpDownload::create_with_job({}, client, (HTTP::HttpJob&)*job, move(output_stream)); - download->set_download_fd(pipe_result.value().read_fd); - job->start(); - return download; -} - -} diff --git a/Services/ProtocolServer/HttpProtocol.h b/Services/ProtocolServer/HttpProtocol.h deleted file mode 100644 index 8c4a564f37..0000000000 --- a/Services/ProtocolServer/HttpProtocol.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <ProtocolServer/Protocol.h> - -namespace ProtocolServer { - -class HttpProtocol final : public Protocol { -public: - HttpProtocol(); - virtual ~HttpProtocol() override; - - virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, ReadonlyBytes body) override; -}; - -} diff --git a/Services/ProtocolServer/HttpsDownload.cpp b/Services/ProtocolServer/HttpsDownload.cpp deleted file mode 100644 index 991dd730d8..0000000000 --- a/Services/ProtocolServer/HttpsDownload.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibHTTP/HttpResponse.h> -#include <LibHTTP/HttpsJob.h> -#include <ProtocolServer/HttpsDownload.h> - -namespace ProtocolServer { - -HttpsDownload::HttpsDownload(ClientConnection& client, NonnullRefPtr<HTTP::HttpsJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream) - : Download(client, move(output_stream)) - , m_job(job) -{ - m_job->on_headers_received = [this](auto& headers, auto response_code) { - if (response_code.has_value()) - set_status_code(response_code.value()); - set_response_headers(headers); - }; - - m_job->on_finish = [this](bool success) { - if (auto* response = m_job->response()) { - set_status_code(response->code()); - set_response_headers(response->headers()); - set_downloaded_size(this->output_stream().size()); - } - - // if we didn't know the total size, pretend that the download finished successfully - // and set the total size to the downloaded size - if (!total_size().has_value()) - did_progress(downloaded_size(), downloaded_size()); - - did_finish(success); - }; - m_job->on_progress = [this](Optional<u32> total, u32 current) { - did_progress(total, current); - }; - m_job->on_certificate_requested = [this](auto&) { - did_request_certificates(); - }; -} - -void HttpsDownload::set_certificate(String certificate, String key) -{ - m_job->set_certificate(move(certificate), move(key)); -} - -HttpsDownload::~HttpsDownload() -{ - m_job->on_finish = nullptr; - m_job->on_progress = nullptr; - m_job->shutdown(); -} - -NonnullOwnPtr<HttpsDownload> HttpsDownload::create_with_job(Badge<HttpsProtocol>, ClientConnection& client, NonnullRefPtr<HTTP::HttpsJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream) -{ - return adopt_own(*new HttpsDownload(client, move(job), move(output_stream))); -} - -} diff --git a/Services/ProtocolServer/HttpsDownload.h b/Services/ProtocolServer/HttpsDownload.h deleted file mode 100644 index 254172b3f7..0000000000 --- a/Services/ProtocolServer/HttpsDownload.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Badge.h> -#include <LibCore/Forward.h> -#include <LibHTTP/HttpsJob.h> -#include <ProtocolServer/Download.h> - -namespace ProtocolServer { - -class HttpsDownload final : public Download { -public: - virtual ~HttpsDownload() override; - static NonnullOwnPtr<HttpsDownload> create_with_job(Badge<HttpsProtocol>, ClientConnection&, NonnullRefPtr<HTTP::HttpsJob>, NonnullOwnPtr<OutputFileStream>&&); - -private: - explicit HttpsDownload(ClientConnection&, NonnullRefPtr<HTTP::HttpsJob>, NonnullOwnPtr<OutputFileStream>&&); - - virtual void set_certificate(String certificate, String key) override; - - NonnullRefPtr<HTTP::HttpsJob> m_job; -}; - -} diff --git a/Services/ProtocolServer/HttpsProtocol.cpp b/Services/ProtocolServer/HttpsProtocol.cpp deleted file mode 100644 index ef8e63fd0d..0000000000 --- a/Services/ProtocolServer/HttpsProtocol.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibHTTP/HttpRequest.h> -#include <LibHTTP/HttpsJob.h> -#include <ProtocolServer/HttpsDownload.h> -#include <ProtocolServer/HttpsProtocol.h> - -namespace ProtocolServer { - -HttpsProtocol::HttpsProtocol() - : Protocol("https") -{ -} - -HttpsProtocol::~HttpsProtocol() -{ -} - -OwnPtr<Download> HttpsProtocol::start_download(ClientConnection& client, const String& method, const URL& url, const HashMap<String, String>& headers, ReadonlyBytes body) -{ - HTTP::HttpRequest request; - if (method.equals_ignoring_case("post")) - request.set_method(HTTP::HttpRequest::Method::POST); - else - request.set_method(HTTP::HttpRequest::Method::GET); - request.set_url(url); - request.set_headers(headers); - request.set_body(body); - - auto pipe_result = get_pipe_for_download(); - if (pipe_result.is_error()) - return {}; - - auto output_stream = make<OutputFileStream>(pipe_result.value().write_fd); - output_stream->make_unbuffered(); - auto job = HTTP::HttpsJob::construct(request, *output_stream); - auto download = HttpsDownload::create_with_job({}, client, (HTTP::HttpsJob&)*job, move(output_stream)); - download->set_download_fd(pipe_result.value().read_fd); - job->start(); - return download; -} - -} diff --git a/Services/ProtocolServer/HttpsProtocol.h b/Services/ProtocolServer/HttpsProtocol.h deleted file mode 100644 index 40e59aa271..0000000000 --- a/Services/ProtocolServer/HttpsProtocol.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2020, The SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <ProtocolServer/Protocol.h> - -namespace ProtocolServer { - -class HttpsProtocol final : public Protocol { -public: - HttpsProtocol(); - virtual ~HttpsProtocol() override; - - virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, ReadonlyBytes body) override; -}; - -} diff --git a/Services/ProtocolServer/Protocol.cpp b/Services/ProtocolServer/Protocol.cpp deleted file mode 100644 index ee8248dfef..0000000000 --- a/Services/ProtocolServer/Protocol.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/HashMap.h> -#include <ProtocolServer/Protocol.h> -#include <fcntl.h> -#include <string.h> - -namespace ProtocolServer { - -static HashMap<String, Protocol*>& all_protocols() -{ - static HashMap<String, Protocol*> map; - return map; -} - -Protocol* Protocol::find_by_name(const String& name) -{ - return all_protocols().get(name).value_or(nullptr); -} - -Protocol::Protocol(const String& name) -{ - all_protocols().set(name, this); -} - -Protocol::~Protocol() -{ - ASSERT_NOT_REACHED(); -} - -Result<Protocol::Pipe, String> Protocol::get_pipe_for_download() -{ - int fd_pair[2] { 0 }; - if (pipe(fd_pair) != 0) { - auto saved_errno = errno; - dbgln("Protocol: pipe() failed: {}", strerror(saved_errno)); - return String { strerror(saved_errno) }; - } - fcntl(fd_pair[1], F_SETFL, fcntl(fd_pair[1], F_GETFL) | O_NONBLOCK); - return Pipe { fd_pair[0], fd_pair[1] }; -} - -} diff --git a/Services/ProtocolServer/Protocol.h b/Services/ProtocolServer/Protocol.h deleted file mode 100644 index 89db931afb..0000000000 --- a/Services/ProtocolServer/Protocol.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/RefPtr.h> -#include <AK/Result.h> -#include <AK/URL.h> -#include <ProtocolServer/Forward.h> - -namespace ProtocolServer { - -class Protocol { -public: - virtual ~Protocol(); - - const String& name() const { return m_name; } - virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, ReadonlyBytes body) = 0; - - static Protocol* find_by_name(const String&); - -protected: - explicit Protocol(const String& name); - struct Pipe { - int read_fd { -1 }; - int write_fd { -1 }; - }; - static Result<Pipe, String> get_pipe_for_download(); - -private: - String m_name; -}; - -} diff --git a/Services/ProtocolServer/ProtocolClient.ipc b/Services/ProtocolServer/ProtocolClient.ipc deleted file mode 100644 index 88f4cfc96d..0000000000 --- a/Services/ProtocolServer/ProtocolClient.ipc +++ /dev/null @@ -1,10 +0,0 @@ -endpoint ProtocolClient = 13 -{ - // Download notifications - DownloadProgress(i32 download_id, Optional<u32> total_size, u32 downloaded_size) =| - DownloadFinished(i32 download_id, bool success, u32 total_size) =| - HeadersBecameAvailable(i32 download_id, IPC::Dictionary response_headers, Optional<u32> status_code) =| - - // Certificate requests - CertificateRequested(i32 download_id) => () -} diff --git a/Services/ProtocolServer/ProtocolServer.ipc b/Services/ProtocolServer/ProtocolServer.ipc deleted file mode 100644 index a89eab506b..0000000000 --- a/Services/ProtocolServer/ProtocolServer.ipc +++ /dev/null @@ -1,13 +0,0 @@ -endpoint ProtocolServer = 9 -{ - // Basic protocol - Greet() => (i32 client_id) - - // Test if a specific protocol is supported, e.g "http" - IsSupportedProtocol(String protocol) => (bool supported) - - // Download API - StartDownload(String method, URL url, IPC::Dictionary request_headers, ByteBuffer request_body) => (i32 download_id, Optional<IPC::File> response_fd) - StopDownload(i32 download_id) => (bool success) - SetCertificate(i32 download_id, String certificate, String key) => (bool success) -} diff --git a/Services/ProtocolServer/main.cpp b/Services/ProtocolServer/main.cpp deleted file mode 100644 index 62fc908dd1..0000000000 --- a/Services/ProtocolServer/main.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibCore/EventLoop.h> -#include <LibCore/LocalServer.h> -#include <LibIPC/ClientConnection.h> -#include <LibTLS/Certificate.h> -#include <ProtocolServer/ClientConnection.h> -#include <ProtocolServer/GeminiProtocol.h> -#include <ProtocolServer/HttpProtocol.h> -#include <ProtocolServer/HttpsProtocol.h> - -int main(int, char**) -{ - if (pledge("stdio inet shared_buffer accept unix rpath cpath fattr sendfd recvfd", nullptr) < 0) { - perror("pledge"); - return 1; - } - - // Ensure the certificates are read out here. - [[maybe_unused]] auto& certs = DefaultRootCACertificates::the(); - - Core::EventLoop event_loop; - // FIXME: Establish a connection to LookupServer and then drop "unix"? - if (pledge("stdio inet shared_buffer accept unix sendfd recvfd", nullptr) < 0) { - perror("pledge"); - return 1; - } - if (unveil("/tmp/portal/lookup", "rw") < 0) { - perror("unveil"); - return 1; - } - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - [[maybe_unused]] auto gemini = new ProtocolServer::GeminiProtocol; - [[maybe_unused]] auto http = new ProtocolServer::HttpProtocol; - [[maybe_unused]] auto https = new ProtocolServer::HttpsProtocol; - - auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server(); - ASSERT(socket); - IPC::new_client_connection<ProtocolServer::ClientConnection>(socket.release_nonnull(), 1); - return event_loop.exec(); -} diff --git a/Services/SystemMenu/CMakeLists.txt b/Services/SystemMenu/CMakeLists.txt deleted file mode 100644 index 8d6a6adee8..0000000000 --- a/Services/SystemMenu/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SOURCES - main.cpp - ShutdownDialog.cpp -) - -serenity_bin(SystemMenu) -target_link_libraries(SystemMenu LibGUI LibDesktop) diff --git a/Services/SystemMenu/ShutdownDialog.cpp b/Services/SystemMenu/ShutdownDialog.cpp deleted file mode 100644 index 5f01b7308e..0000000000 --- a/Services/SystemMenu/ShutdownDialog.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ShutdownDialog.h" -#include <AK/String.h> -#include <AK/Vector.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Label.h> -#include <LibGUI/RadioButton.h> -#include <LibGUI/Widget.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> - -struct Option { - String title; - Vector<char const*> cmd; - bool enabled; - bool default_action; -}; - -static const Vector<Option> options = { - { "Shut down", { "/bin/shutdown", "--now", nullptr }, true, true }, - { "Restart", { "/bin/reboot", nullptr }, true, false }, - { "Log out", {}, false, false }, - { "Sleep", {}, false, false }, -}; - -Vector<char const*> ShutdownDialog::show() -{ - auto dialog = ShutdownDialog::construct(); - auto rc = dialog->exec(); - if (rc == ExecResult::ExecOK && dialog->m_selected_option != -1) - return options[dialog->m_selected_option].cmd; - - return {}; -} - -ShutdownDialog::ShutdownDialog() - : Dialog(nullptr) -{ - resize(180, 180 + ((static_cast<int>(options.size()) - 3) * 16)); - center_on_screen(); - set_resizable(false); - set_title("SerenityOS"); - set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/power.png")); - - auto& main = set_main_widget<GUI::Widget>(); - main.set_layout<GUI::VerticalBoxLayout>(); - main.layout()->set_margins({ 8, 8, 8, 8 }); - main.layout()->set_spacing(8); - main.set_fill_with_background_color(true); - - auto& header = main.add<GUI::Label>(); - header.set_text("What would you like to do?"); - header.set_fixed_height(16); - header.set_font(Gfx::FontDatabase::default_bold_font()); - - for (size_t i = 0; i < options.size(); i++) { - auto action = options[i]; - auto& radio = main.add<GUI::RadioButton>(); - radio.set_enabled(action.enabled); - radio.set_text(action.title); - - radio.on_checked = [this, i](auto) { - m_selected_option = i; - }; - - if (action.default_action) { - radio.set_checked(true); - m_selected_option = i; - } - } - - auto& button_box = main.add<GUI::Widget>(); - button_box.set_layout<GUI::HorizontalBoxLayout>(); - button_box.layout()->set_spacing(8); - - auto& ok_button = button_box.add<GUI::Button>(); - ok_button.on_click = [this](auto) { - done(ExecResult::ExecOK); - }; - ok_button.set_text("OK"); - - auto& cancel_button = button_box.add<GUI::Button>(); - cancel_button.on_click = [this](auto) { - done(ExecResult::ExecCancel); - }; - cancel_button.set_text("Cancel"); -} - -ShutdownDialog::~ShutdownDialog() -{ -} diff --git a/Services/SystemMenu/ShutdownDialog.h b/Services/SystemMenu/ShutdownDialog.h deleted file mode 100644 index 73f581916c..0000000000 --- a/Services/SystemMenu/ShutdownDialog.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Vector.h> -#include <LibGUI/Dialog.h> - -class ShutdownDialog : public GUI::Dialog { - C_OBJECT(ShutdownDialog); - -public: - static Vector<char const*> show(); - -private: - ShutdownDialog(); - virtual ~ShutdownDialog() override; - - int m_selected_option { -1 }; -}; diff --git a/Services/SystemMenu/main.cpp b/Services/SystemMenu/main.cpp deleted file mode 100644 index af532ee4cf..0000000000 --- a/Services/SystemMenu/main.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ShutdownDialog.h" -#include <AK/LexicalPath.h> -#include <AK/QuickSort.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/DirIterator.h> -#include <LibCore/StandardPaths.h> -#include <LibDesktop/AppFile.h> -#include <LibGUI/Action.h> -#include <LibGUI/ActionGroup.h> -#include <LibGUI/Application.h> -#include <LibGUI/FileIconProvider.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Menu.h> -#include <LibGUI/WindowServerConnection.h> -#include <LibGfx/Bitmap.h> -#include <serenity.h> -#include <spawn.h> - -//#define SYSTEM_MENU_DEBUG - -struct AppMetadata { - String executable; - String name; - String category; -}; -Vector<AppMetadata> g_apps; - -struct ThemeMetadata { - String name; - String path; -}; - -Color g_menu_selection_color; - -Vector<ThemeMetadata> g_themes; -RefPtr<GUI::Menu> g_themes_menu; -GUI::ActionGroup g_themes_group; - -static Vector<String> discover_apps_and_categories(); -static NonnullRefPtr<GUI::Menu> build_system_menu(); - -int main(int argc, char** argv) -{ - auto app = GUI::Application::construct(argc, argv); - app->set_quit_when_last_window_deleted(false); - - auto menu = build_system_menu(); - menu->realize_menu_if_needed(); - - GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetSystemMenu>(menu->menu_id()); - - if (pledge("stdio shared_buffer accept rpath proc exec", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (chdir(Core::StandardPaths::home_directory().characters()) < 0) { - perror("chdir"); - return 1; - } - - if (unveil("/bin", "x")) { - perror("unveil"); - return 1; - } - - if (unveil("/res", "r")) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - return app->exec(); -} - -Vector<String> discover_apps_and_categories() -{ - HashTable<String> seen_app_categories; - Desktop::AppFile::for_each([&](auto af) { - g_apps.append({ af->executable(), af->name(), af->category() }); - seen_app_categories.set(af->category()); - }); - quick_sort(g_apps, [](auto& a, auto& b) { return a.name < b.name; }); - - Vector<String> sorted_app_categories; - for (auto& category : seen_app_categories) { - sorted_app_categories.append(category); - } - quick_sort(sorted_app_categories); - - return sorted_app_categories; -} - -NonnullRefPtr<GUI::Menu> build_system_menu() -{ - const Vector<String> sorted_app_categories = discover_apps_and_categories(); - auto system_menu = GUI::Menu::construct("\xE2\x9A\xA1"); // HIGH VOLTAGE SIGN - - // First we construct all the necessary app category submenus. - HashMap<String, NonnullRefPtr<GUI::Menu>> app_category_menus; - auto category_icons = Core::ConfigFile::open("/res/icons/SystemMenu.ini"); - for (const auto& category : sorted_app_categories) { - if (app_category_menus.contains(category)) - continue; - auto& category_menu = system_menu->add_submenu(category); - auto category_icon_path = category_icons->read_entry("16x16", category); - if (!category_icon_path.is_empty()) { - auto icon = Gfx::Bitmap::load_from_file(category_icon_path); - category_menu.set_icon(icon); - } - app_category_menus.set(category, category_menu); - } - - // Then we create and insert all the app menu items into the right place. - int app_identifier = 0; - for (const auto& app : g_apps) { - auto icon = GUI::FileIconProvider::icon_for_executable(app.executable).bitmap_for_size(16); - -#ifdef SYSTEM_MENU_DEBUG - if (icon) - dbg() << "App " << app.name << " has icon with size " << icon->size(); -#endif - - auto parent_menu = app_category_menus.get(app.category).value_or(*system_menu); - parent_menu->add_action(GUI::Action::create(app.name, icon, [app_identifier](auto&) { - dbg() << "Activated app with ID " << app_identifier; - const auto& bin = g_apps[app_identifier].executable; - pid_t child_pid; - const char* argv[] = { bin.characters(), nullptr }; - if ((errno = posix_spawn(&child_pid, bin.characters(), nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - } else { - if (disown(child_pid) < 0) - perror("disown"); - } - })); - ++app_identifier; - } - - system_menu->add_separator(); - - g_themes_group.set_exclusive(true); - g_themes_group.set_unchecking_allowed(false); - - g_themes_menu = &system_menu->add_submenu("Themes"); - g_themes_menu->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/themes.png")); - - { - Core::DirIterator dt("/res/themes", Core::DirIterator::SkipDots); - while (dt.has_next()) { - auto theme_name = dt.next_path(); - auto theme_path = String::format("/res/themes/%s", theme_name.characters()); - g_themes.append({ LexicalPath(theme_name).title(), theme_path }); - } - quick_sort(g_themes, [](auto& a, auto& b) { return a.name < b.name; }); - } - - auto current_theme_name = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::GetSystemTheme>()->theme_name(); - - { - int theme_identifier = 0; - for (auto& theme : g_themes) { - auto action = GUI::Action::create_checkable(theme.name, [theme_identifier](auto&) { - auto& theme = g_themes[theme_identifier]; - dbg() << "Theme switched to " << theme.name << " at path " << theme.path; - auto response = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetSystemTheme>(theme.path, theme.name); - ASSERT(response->success()); - }); - if (theme.name == current_theme_name) - action->set_checked(true); - g_themes_group.add_action(action); - g_themes_menu->add_action(action); - ++theme_identifier; - } - } - - system_menu->add_separator(); - system_menu->add_action(GUI::Action::create("About SerenityOS", Gfx::Bitmap::load_from_file("/res/icons/16x16/ladybug.png"), [](auto&) { - pid_t child_pid; - const char* argv[] = { "/bin/About", nullptr }; - if ((errno = posix_spawn(&child_pid, "/bin/About", nullptr, nullptr, const_cast<char**>(argv), environ))) { - perror("posix_spawn"); - } else { - if (disown(child_pid) < 0) - perror("disown"); - } - })); - system_menu->add_separator(); - system_menu->add_action(GUI::Action::create("Exit...", Gfx::Bitmap::load_from_file("/res/icons/16x16/power.png"), [](auto&) { - auto command = ShutdownDialog::show(); - - if (command.size() == 0) - return; - - pid_t child_pid; - if ((errno = posix_spawn(&child_pid, command[0], nullptr, nullptr, const_cast<char**>(command.data()), environ))) { - perror("posix_spawn"); - } else { - if (disown(child_pid) < 0) - perror("disown"); - } - })); - - return system_menu; -} diff --git a/Services/SystemServer/CMakeLists.txt b/Services/SystemServer/CMakeLists.txt deleted file mode 100644 index fda6eb1bcc..0000000000 --- a/Services/SystemServer/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SOURCES - main.cpp - Service.cpp -) - -serenity_bin(SystemServer) -target_link_libraries(SystemServer LibCore) diff --git a/Services/SystemServer/Service.cpp b/Services/SystemServer/Service.cpp deleted file mode 100644 index 99b01b5ab6..0000000000 --- a/Services/SystemServer/Service.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Service.h" -#include <AK/HashMap.h> -#include <AK/JsonArray.h> -#include <AK/JsonObject.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/File.h> -#include <LibCore/Socket.h> -#include <grp.h> -#include <libgen.h> -#include <pwd.h> -#include <sched.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <unistd.h> - -static HashMap<pid_t, Service*> s_service_map; - -Service* Service::find_by_pid(pid_t pid) -{ - auto it = s_service_map.find(pid); - if (it == s_service_map.end()) - return nullptr; - return (*it).value; -} - -void Service::setup_socket() -{ - ASSERT(!m_socket_path.is_null()); - ASSERT(m_socket_fd == -1); - - auto ok = Core::File::ensure_parent_directories(m_socket_path); - ASSERT(ok); - - // Note: we use SOCK_CLOEXEC here to make sure we don't leak every socket to - // all the clients. We'll make the one we do need to pass down !CLOEXEC later - // after forking off the process. - m_socket_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); - if (m_socket_fd < 0) { - perror("socket"); - ASSERT_NOT_REACHED(); - } - - if (m_account.has_value()) { - auto& account = m_account.value(); - if (fchown(m_socket_fd, account.uid(), account.gid()) < 0) { - perror("fchown"); - ASSERT_NOT_REACHED(); - } - } - - if (fchmod(m_socket_fd, m_socket_permissions) < 0) { - perror("fchmod"); - ASSERT_NOT_REACHED(); - } - - auto socket_address = Core::SocketAddress::local(m_socket_path); - auto un_optional = socket_address.to_sockaddr_un(); - if (!un_optional.has_value()) { - dbg() << "Socket name " << m_socket_path << " is too long. BUG! This should have failed earlier!"; - ASSERT_NOT_REACHED(); - } - auto un = un_optional.value(); - int rc = bind(m_socket_fd, (const sockaddr*)&un, sizeof(un)); - if (rc < 0) { - perror("bind"); - ASSERT_NOT_REACHED(); - } - - rc = listen(m_socket_fd, 16); - if (rc < 0) { - perror("listen"); - ASSERT_NOT_REACHED(); - } -} - -void Service::setup_notifier() -{ - ASSERT(m_lazy); - ASSERT(m_socket_fd >= 0); - ASSERT(!m_socket_notifier); - - m_socket_notifier = Core::Notifier::construct(m_socket_fd, Core::Notifier::Event::Read, this); - m_socket_notifier->on_ready_to_read = [this] { - handle_socket_connection(); - }; -} - -void Service::handle_socket_connection() -{ -#ifdef SERVICE_DEBUG - dbg() << "Ready to read on behalf of " << name(); -#endif - if (m_accept_socket_connections) { - int accepted_fd = accept(m_socket_fd, nullptr, nullptr); - if (accepted_fd < 0) { - perror("accept"); - return; - } - spawn(accepted_fd); - close(accepted_fd); - } else { - remove_child(*m_socket_notifier); - m_socket_notifier = nullptr; - spawn(m_socket_fd); - } -} - -void Service::activate() -{ - ASSERT(m_pid < 0); - - if (m_lazy) - setup_notifier(); - else - spawn(m_socket_fd); -} - -void Service::spawn(int socket_fd) -{ -#ifdef SERVICE_DEBUG - dbg() << "Spawning " << name(); -#endif - - m_run_timer.start(); - pid_t pid = fork(); - - if (pid < 0) { - perror("fork"); - dbg() << "Failed to spawn " << name() << ". Sucks, dude :("; - } else if (pid == 0) { - // We are the child. - - if (!m_working_directory.is_null()) { - if (chdir(m_working_directory.characters()) < 0) { - perror("chdir"); - ASSERT_NOT_REACHED(); - } - } - - struct sched_param p; - p.sched_priority = m_priority; - int rc = sched_setparam(0, &p); - if (rc < 0) { - perror("sched_setparam"); - ASSERT_NOT_REACHED(); - } - - if (!m_stdio_file_path.is_null()) { - close(STDIN_FILENO); - int fd = open_with_path_length(m_stdio_file_path.characters(), m_stdio_file_path.length(), O_RDWR, 0); - ASSERT(fd <= 0); - if (fd < 0) { - perror("open"); - ASSERT_NOT_REACHED(); - } - dup2(STDIN_FILENO, STDOUT_FILENO); - dup2(STDIN_FILENO, STDERR_FILENO); - - if (isatty(STDIN_FILENO)) { - ioctl(STDIN_FILENO, TIOCSCTTY); - } - } else { - if (isatty(STDIN_FILENO)) { - ioctl(STDIN_FILENO, TIOCNOTTY); - } - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - - int fd = open("/dev/null", O_RDWR); - ASSERT(fd == STDIN_FILENO); - dup2(STDIN_FILENO, STDOUT_FILENO); - dup2(STDIN_FILENO, STDERR_FILENO); - } - - if (socket_fd >= 0) { - ASSERT(!m_socket_path.is_null()); - ASSERT(socket_fd > 3); - dup2(socket_fd, 3); - // The new descriptor is !CLOEXEC here. - setenv("SOCKET_TAKEOVER", "1", true); - } - - if (m_account.has_value()) { - auto& account = m_account.value(); - if (setgid(account.gid()) < 0 || setgroups(account.extra_gids().size(), account.extra_gids().data()) < 0 || setuid(account.uid()) < 0) { - dbgln("Failed to drop privileges (GID={}, UID={})\n", account.gid(), account.uid()); - exit(1); - } - setenv("HOME", account.home_directory().characters(), true); - } - - for (String& env : m_environment) - putenv(const_cast<char*>(env.characters())); - - char* argv[m_extra_arguments.size() + 2]; - argv[0] = const_cast<char*>(m_executable_path.characters()); - for (size_t i = 0; i < m_extra_arguments.size(); i++) - argv[i + 1] = const_cast<char*>(m_extra_arguments[i].characters()); - argv[m_extra_arguments.size() + 1] = nullptr; - - rc = execv(argv[0], argv); - perror("exec"); - ASSERT_NOT_REACHED(); - } else if (!m_multi_instance) { - // We are the parent. - m_pid = pid; - s_service_map.set(pid, this); - } -} - -void Service::did_exit(int exit_code) -{ - ASSERT(m_pid > 0); - ASSERT(!m_multi_instance); - - dbg() << "Service " << name() << " has exited with exit code " << exit_code; - - s_service_map.remove(m_pid); - m_pid = -1; - - if (!m_keep_alive) - return; - - int run_time_in_msec = m_run_timer.elapsed(); - bool exited_successfully = exit_code == 0; - - if (!exited_successfully && run_time_in_msec < 1000) { - switch (m_restart_attempts) { - case 0: - dbgln("Trying again"); - break; - case 1: - dbgln("Third time's a charm?"); - break; - default: - dbg() << "Giving up on " << name() << ". Good luck!"; - return; - } - m_restart_attempts++; - } - - activate(); -} - -Service::Service(const Core::ConfigFile& config, const StringView& name) - : Core::Object(nullptr) -{ - ASSERT(config.has_group(name)); - - set_name(name); - m_executable_path = config.read_entry(name, "Executable", String::format("/bin/%s", this->name().characters())); - m_extra_arguments = config.read_entry(name, "Arguments", "").split(' '); - m_stdio_file_path = config.read_entry(name, "StdIO"); - - String prio = config.read_entry(name, "Priority"); - if (prio == "low") - m_priority = 10; - else if (prio == "normal" || prio.is_null()) - m_priority = 30; - else if (prio == "high") - m_priority = 50; - else - ASSERT_NOT_REACHED(); - - m_keep_alive = config.read_bool_entry(name, "KeepAlive"); - m_lazy = config.read_bool_entry(name, "Lazy"); - - m_user = config.read_entry(name, "User"); - if (!m_user.is_null()) { - auto result = Core::Account::from_name(m_user.characters()); - if (result.is_error()) - warnln("Failed to resolve user {}: {}", m_user, result.error()); - else - m_account = result.value(); - } - - m_working_directory = config.read_entry(name, "WorkingDirectory"); - m_environment = config.read_entry(name, "Environment").split(' '); - m_boot_modes = config.read_entry(name, "BootModes", "graphical").split(','); - m_multi_instance = config.read_bool_entry(name, "MultiInstance"); - m_accept_socket_connections = config.read_bool_entry(name, "AcceptSocketConnections"); - - m_socket_path = config.read_entry(name, "Socket"); - - // Lazy requires Socket. - ASSERT(!m_lazy || !m_socket_path.is_null()); - // AcceptSocketConnections always requires Socket, Lazy, and MultiInstance. - ASSERT(!m_accept_socket_connections || (!m_socket_path.is_null() && m_lazy && m_multi_instance)); - // MultiInstance doesn't work with KeepAlive. - ASSERT(!m_multi_instance || !m_keep_alive); - // Socket path (plus NUL) must fit into the structs sent to the Kernel. - ASSERT(m_socket_path.length() < UNIX_PATH_MAX); - - if (!m_socket_path.is_null() && is_enabled()) { - auto socket_permissions_string = config.read_entry(name, "SocketPermissions", "0600"); - m_socket_permissions = strtol(socket_permissions_string.characters(), nullptr, 8) & 04777; - setup_socket(); - } -} - -void Service::save_to(JsonObject& json) -{ - Core::Object::save_to(json); - - json.set("executable_path", m_executable_path); - - // FIXME: This crashes Inspector. - /* - JsonArray extra_args; - for (String& arg : m_extra_arguments) - extra_args.append(arg); - json.set("extra_arguments", move(extra_args)); - - JsonArray boot_modes; - for (String& mode : m_boot_modes) - boot_modes.append(mode); - json.set("boot_modes", boot_modes); - - JsonArray environment; - for (String& env : m_environment) - boot_modes.append(env); - json.set("environment", environment); - */ - - json.set("stdio_file_path", m_stdio_file_path); - json.set("priority", m_priority); - json.set("keep_alive", m_keep_alive); - json.set("socket_path", m_socket_path); - json.set("socket_permissions", m_socket_permissions); - json.set("lazy", m_lazy); - json.set("user", m_user); - json.set("multi_instance", m_multi_instance); - json.set("accept_socket_connections", m_accept_socket_connections); - - if (m_pid > 0) - json.set("pid", m_pid); - else - json.set("pid", nullptr); - - json.set("restart_attempts", m_restart_attempts); - json.set("working_directory", m_working_directory); -} - -bool Service::is_enabled() const -{ - extern String g_boot_mode; - return m_boot_modes.contains_slow(g_boot_mode); -} diff --git a/Services/SystemServer/Service.h b/Services/SystemServer/Service.h deleted file mode 100644 index 23014825cf..0000000000 --- a/Services/SystemServer/Service.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/RefPtr.h> -#include <AK/String.h> -#include <LibCore/Account.h> -#include <LibCore/ElapsedTimer.h> -#include <LibCore/Notifier.h> -#include <LibCore/Object.h> - -class Service final : public Core::Object { - C_OBJECT(Service) - -public: - bool is_enabled() const; - void activate(); - void did_exit(int exit_code); - - static Service* find_by_pid(pid_t); - - // FIXME: Port to Core::Property - void save_to(AK::JsonObject&); - -private: - Service(const Core::ConfigFile&, const StringView& name); - - void spawn(int socket_fd = -1); - - // Path to the executable. By default this is /bin/{m_name}. - String m_executable_path; - // Extra arguments, starting from argv[1], to pass when exec'ing. - Vector<String> m_extra_arguments; - // File path to open as stdio fds. - String m_stdio_file_path; - int m_priority { 1 }; - // Whether we should re-launch it if it exits. - bool m_keep_alive { false }; - // Path to the socket to create and listen on on behalf of this service. - String m_socket_path; - // File system permissions for the socket. - mode_t m_socket_permissions { 0 }; - // Whether we should accept connections on the socket and pass the accepted - // (and not listening) socket to the service. This requires a multi-instance - // service. - bool m_accept_socket_connections { false }; - // Whether we should only spawn this service once somebody connects to the socket. - bool m_lazy; - // The name of the user we should run this service as. - String m_user; - // The working directory in which to spawn the service. - String m_working_directory; - // Boot modes to run this service in. By default, this is the graphical mode. - Vector<String> m_boot_modes; - // Whether several instances of this service can run at once. - bool m_multi_instance { false }; - // Environment variables to pass to the service. - Vector<String> m_environment; - - // The resolved user account to run this service as. - Optional<Core::Account> m_account; - - // For single-instance services, PID of the running instance of this service. - pid_t m_pid { -1 }; - // An open fd to the socket. - int m_socket_fd { -1 }; - RefPtr<Core::Notifier> m_socket_notifier; - - // Timer since we last spawned the service. - Core::ElapsedTimer m_run_timer; - // How many times we have tried to restart this service, only counting those - // times where it has exited unsuccessfully and too quickly. - int m_restart_attempts { 0 }; - - void setup_socket(); - void setup_notifier(); - void handle_socket_connection(); -}; diff --git a/Services/SystemServer/main.cpp b/Services/SystemServer/main.cpp deleted file mode 100644 index 6409b0eda6..0000000000 --- a/Services/SystemServer/main.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Service.h" -#include <AK/Assertions.h> -#include <AK/ByteBuffer.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/Event.h> -#include <LibCore/EventLoop.h> -#include <LibCore/File.h> -#include <errno.h> -#include <signal.h> -#include <stdio.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -String g_boot_mode = "graphical"; - -static void sigchld_handler(int) -{ - for (;;) { - int status = 0; - pid_t pid = waitpid(-1, &status, WNOHANG); - if (pid < 0) { - perror("waitpid"); - break; - } - if (pid == 0) - break; - -#ifdef SYSTEMSERVER_DEBUG - dbg() << "Reaped child with pid " << pid << ", exit status " << status; -#endif - - Service* service = Service::find_by_pid(pid); - if (service == nullptr) { - // This can happen for multi-instance services. - continue; - } - - service->did_exit(status); - } -} - -static void parse_boot_mode() -{ - auto f = Core::File::construct("/proc/cmdline"); - if (!f->open(Core::IODevice::ReadOnly)) { - dbg() << "Failed to read command line: " << f->error_string(); - return; - } - const String cmdline = String::copy(f->read_all(), Chomp); - dbg() << "Read command line: " << cmdline; - - for (auto& part : cmdline.split_view(' ')) { - auto pair = part.split_view('=', 2); - if (pair.size() == 2 && pair[0] == "boot_mode") - g_boot_mode = pair[1]; - } - dbg() << "Booting in " << g_boot_mode << " mode"; -} - -static void prepare_devfs() -{ - // FIXME: Find a better way to all of this stuff, without hardcoding all of this! - - int rc = mount(-1, "/dev", "dev", 0); - if (rc != 0) { - ASSERT_NOT_REACHED(); - } - - rc = mkdir("/dev/pts", 0755); - if (rc != 0) { - ASSERT_NOT_REACHED(); - } - - rc = mount(-1, "/dev/pts", "devpts", 0); - if (rc != 0) { - ASSERT_NOT_REACHED(); - } - - rc = symlink("/dev/random", "/dev/urandom"); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - - // FIXME: Find a better way to chown without hardcoding the gid! - // This will fail with ENOENT in text mode. - rc = chown("/dev/fb0", 0, 3); - if (rc < 0 && errno != ENOENT) { - ASSERT_NOT_REACHED(); - } - - // FIXME: Find a better way to chown without hardcoding the gid! - rc = chown("/dev/keyboard", 0, 3); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - - // FIXME: Find a better way to chown without hardcoding the gid! - rc = chown("/dev/mouse", 0, 3); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - - for (size_t index = 0; index < 4; index++) { - // FIXME: Find a better way to chown without hardcoding the gid! - rc = chown(String::formatted("/dev/tty{}", index).characters(), 0, 2); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - } - - for (size_t index = 0; index < 4; index++) { - // FIXME: Find a better way to chown without hardcoding the gid! - rc = chown(String::formatted("/dev/ttyS{}", index).characters(), 0, 2); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - } - - // FIXME: Find a better way to chown without hardcoding the gid! - rc = chown("/dev/audio", 0, 4); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - - rc = symlink("/proc/self/fd/0", "/dev/stdin"); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - rc = symlink("/proc/self/fd/1", "/dev/stdout"); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - rc = symlink("/proc/self/fd/2", "/dev/stderr"); - if (rc < 0) { - ASSERT_NOT_REACHED(); - } -} - -static void mount_all_filesystems() -{ - dbgln("Spawning mount -a to mount all filesystems."); - pid_t pid = fork(); - - if (pid < 0) { - perror("fork"); - ASSERT_NOT_REACHED(); - } else if (pid == 0) { - execl("/bin/mount", "mount", "-a", nullptr); - perror("exec"); - ASSERT_NOT_REACHED(); - } else { - wait(nullptr); - } -} - -static void create_tmp_rpc_directory() -{ - dbgln("Creating /tmp/rpc directory"); - auto old_umask = umask(0); - auto rc = mkdir("/tmp/rpc", 01777); - if (rc < 0) { - perror("mkdir(/tmp/rpc)"); - ASSERT_NOT_REACHED(); - } - umask(old_umask); -} - -static void create_tmp_coredump_directory() -{ - dbgln("Creating /tmp/coredump directory"); - auto old_umask = umask(0); - auto rc = mkdir("/tmp/coredump", 0755); - if (rc < 0) { - perror("mkdir(/tmp/coredump)"); - ASSERT_NOT_REACHED(); - } - umask(old_umask); -} - -int main(int, char**) -{ - prepare_devfs(); - - if (pledge("stdio proc exec tty accept unix rpath wpath cpath chown fattr id sigaction", nullptr) < 0) { - perror("pledge"); - return 1; - } - - mount_all_filesystems(); - create_tmp_rpc_directory(); - create_tmp_coredump_directory(); - parse_boot_mode(); - - Core::EventLoop event_loop; - - event_loop.register_signal(SIGCHLD, sigchld_handler); - - // Read our config and instantiate services. - // This takes care of setting up sockets. - NonnullRefPtrVector<Service> services; - auto config = Core::ConfigFile::get_for_system("SystemServer"); - for (auto name : config->groups()) { - auto service = Service::construct(*config, name); - if (service->is_enabled()) - services.append(service); - } - - // After we've set them all up, activate them! - dbg() << "Activating " << services.size() << " services..."; - for (auto& service : services) - service.activate(); - - return event_loop.exec(); -} diff --git a/Services/Taskbar/CMakeLists.txt b/Services/Taskbar/CMakeLists.txt deleted file mode 100644 index 83de9ee9c8..0000000000 --- a/Services/Taskbar/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(SOURCES - main.cpp - TaskbarButton.cpp - TaskbarWindow.cpp - WindowList.cpp -) - -serenity_bin(Taskbar) -target_link_libraries(Taskbar LibGUI LibDesktop) diff --git a/Services/Taskbar/TaskbarButton.cpp b/Services/Taskbar/TaskbarButton.cpp deleted file mode 100644 index 69103ca5e0..0000000000 --- a/Services/Taskbar/TaskbarButton.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "TaskbarButton.h" -#include "WindowList.h" -#include <LibGUI/Action.h> -#include <LibGUI/Painter.h> -#include <LibGUI/WindowServerConnection.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> -#include <LibGfx/Palette.h> -#include <LibGfx/StylePainter.h> - -TaskbarButton::TaskbarButton(const WindowIdentifier& identifier) - : m_identifier(identifier) -{ -} - -TaskbarButton::~TaskbarButton() -{ -} - -void TaskbarButton::context_menu_event(GUI::ContextMenuEvent&) -{ - GUI::WindowServerConnection::the().post_message(Messages::WindowServer::WM_PopupWindowMenu(m_identifier.client_id(), m_identifier.window_id(), screen_relative_rect().location())); -} - -void TaskbarButton::update_taskbar_rect() -{ - GUI::WindowServerConnection::the().post_message( - Messages::WindowServer::WM_SetWindowTaskbarRect( - m_identifier.client_id(), - m_identifier.window_id(), - screen_relative_rect())); -} - -void TaskbarButton::clear_taskbar_rect() -{ - GUI::WindowServerConnection::the().post_message( - Messages::WindowServer::WM_SetWindowTaskbarRect( - m_identifier.client_id(), - m_identifier.window_id(), - {})); -} - -void TaskbarButton::resize_event(GUI::ResizeEvent& event) -{ - update_taskbar_rect(); - return GUI::Button::resize_event(event); -} - -static void paint_custom_progress_bar(GUI::Painter& painter, const Gfx::IntRect& rect, const Gfx::IntRect& text_rect, const Palette& palette, int min, int max, int value, const StringView& text, const Gfx::Font& font, Gfx::TextAlignment text_alignment) -{ - float range_size = max - min; - float progress = (value - min) / range_size; - float progress_width = progress * rect.width(); - - Gfx::IntRect progress_rect { rect.x(), rect.y(), (int)progress_width, rect.height() }; - - { - Gfx::PainterStateSaver saver(painter); - painter.add_clip_rect(progress_rect); - - Color start_color = palette.active_window_border1(); - Color end_color = palette.active_window_border2(); - painter.fill_rect_with_gradient(rect, start_color, end_color); - - if (!text.is_null()) { - painter.draw_text(text_rect.translated(1, 1), text, font, text_alignment, palette.base_text(), Gfx::TextElision::Right); - painter.draw_text(text_rect, text, font, text_alignment, palette.base_text().inverted(), Gfx::TextElision::Right); - } - } - - Gfx::IntRect hole_rect { (int)progress_width, 0, (int)(rect.width() - progress_width), rect.height() }; - hole_rect.move_by(rect.location()); - hole_rect.set_right_without_resize(rect.right()); - Gfx::PainterStateSaver saver(painter); - painter.add_clip_rect(hole_rect); - if (!text.is_null()) - painter.draw_text(text_rect, text, font, text_alignment, palette.base_text(), Gfx::TextElision::Right); -} - -void TaskbarButton::paint_event(GUI::PaintEvent& event) -{ - ASSERT(icon()); - auto& icon = *this->icon(); - auto& font = is_checked() ? Gfx::FontDatabase::default_bold_font() : this->font(); - auto& window = WindowList::the().ensure_window(m_identifier); - - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - - Gfx::StylePainter::paint_button(painter, rect(), palette(), button_style(), is_being_pressed(), is_hovered(), is_checked(), is_enabled()); - - if (text().is_empty()) - return; - - bool has_progress = window.progress() >= 0 && window.progress() <= 100; - - auto content_rect = rect().shrunken(8, 2); - auto icon_location = content_rect.center().translated(-(icon.width() / 2), -(icon.height() / 2)); - if (!text().is_empty()) - icon_location.set_x(content_rect.x()); - - if (!text().is_empty()) { - content_rect.move_by(icon.width() + 4, 0); - content_rect.set_width(content_rect.width() - icon.width() - 4); - } - - Gfx::IntRect text_rect { 0, 0, font.width(text()), font.glyph_height() }; - if (text_rect.width() > content_rect.width()) - text_rect.set_width(content_rect.width()); - text_rect.align_within(content_rect, text_alignment()); - - if (is_being_pressed() || is_checked()) { - text_rect.move_by(1, 1); - icon_location.move_by(1, 1); - } - - if (has_progress) { - auto adjusted_rect = rect().shrunken(4, 4); - if (is_being_pressed() || is_checked()) { - adjusted_rect.set_height(adjusted_rect.height() + 1); - } - paint_custom_progress_bar(painter, adjusted_rect, text_rect, palette(), 0, 100, window.progress(), text(), font, text_alignment()); - } - - if (is_enabled()) { - if (is_hovered()) - painter.blit_brightened(icon_location, icon, icon.rect()); - else - painter.blit(icon_location, icon, icon.rect()); - } else { - painter.blit_disabled(icon_location, icon, icon.rect(), palette()); - } - - if (!has_progress) - paint_text(painter, text_rect, font, text_alignment()); -} diff --git a/Services/Taskbar/TaskbarButton.h b/Services/Taskbar/TaskbarButton.h deleted file mode 100644 index b990c04d03..0000000000 --- a/Services/Taskbar/TaskbarButton.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "WindowIdentifier.h" -#include <LibGUI/Button.h> - -class TaskbarButton final : public GUI::Button { - C_OBJECT(TaskbarButton) -public: - virtual ~TaskbarButton() override; - - void update_taskbar_rect(); - void clear_taskbar_rect(); - -private: - explicit TaskbarButton(const WindowIdentifier&); - - virtual void context_menu_event(GUI::ContextMenuEvent&) override; - virtual void resize_event(GUI::ResizeEvent&) override; - virtual void paint_event(GUI::PaintEvent&) override; - - WindowIdentifier m_identifier; -}; diff --git a/Services/Taskbar/TaskbarWindow.cpp b/Services/Taskbar/TaskbarWindow.cpp deleted file mode 100644 index fbc7946634..0000000000 --- a/Services/Taskbar/TaskbarWindow.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "TaskbarWindow.h" -#include "TaskbarButton.h" -#include <AK/SharedBuffer.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/StandardPaths.h> -#include <LibDesktop/AppFile.h> -#include <LibGUI/BoxLayout.h> -#include <LibGUI/Button.h> -#include <LibGUI/Desktop.h> -#include <LibGUI/Frame.h> -#include <LibGUI/Icon.h> -#include <LibGUI/Painter.h> -#include <LibGUI/Window.h> -#include <LibGUI/WindowServerConnection.h> -#include <LibGfx/Palette.h> -#include <serenity.h> -#include <stdio.h> - -//#define EVENT_DEBUG - -class TaskbarWidget final : public GUI::Widget { - C_OBJECT(TaskbarWidget); - -public: - virtual ~TaskbarWidget() override { } - -private: - TaskbarWidget() { } - - virtual void paint_event(GUI::PaintEvent& event) override - { - GUI::Painter painter(*this); - painter.add_clip_rect(event.rect()); - painter.fill_rect(rect(), palette().button()); - painter.draw_line({ 0, 1 }, { width() - 1, 1 }, palette().threed_highlight()); - } - - virtual void did_layout() override - { - WindowList::the().for_each_window([&](auto& window) { - if (auto* button = window.button()) - static_cast<TaskbarButton*>(button)->update_taskbar_rect(); - }); - } -}; - -TaskbarWindow::TaskbarWindow() -{ - set_window_type(GUI::WindowType::Taskbar); - set_title("Taskbar"); - - on_screen_rect_change(GUI::Desktop::the().rect()); - - GUI::Desktop::the().on_rect_change = [this](const Gfx::IntRect& rect) { on_screen_rect_change(rect); }; - - auto& widget = set_main_widget<TaskbarWidget>(); - widget.set_layout<GUI::HorizontalBoxLayout>(); - widget.layout()->set_margins({ 3, 2, 3, 2 }); - widget.layout()->set_spacing(3); - - m_default_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"); - - create_quick_launch_bar(); -} - -TaskbarWindow::~TaskbarWindow() -{ -} - -void TaskbarWindow::create_quick_launch_bar() -{ - auto& quick_launch_bar = main_widget()->add<GUI::Frame>(); - quick_launch_bar.set_layout<GUI::HorizontalBoxLayout>(); - quick_launch_bar.layout()->set_spacing(0); - quick_launch_bar.layout()->set_margins({ 3, 0, 3, 0 }); - quick_launch_bar.set_frame_thickness(0); - - int total_width = 6; - bool first = true; - - auto config = Core::ConfigFile::get_for_app("Taskbar"); - constexpr const char* quick_launch = "QuickLaunch"; - - // FIXME: Core::ConfigFile does not keep the order of the entries. - for (auto& name : config->keys(quick_launch)) { - auto af_name = config->read_entry(quick_launch, name); - auto af_path = String::formatted("{}/{}", Desktop::AppFile::APP_FILES_DIRECTORY, af_name); - auto af = Desktop::AppFile::open(af_path); - if (!af->is_valid()) - continue; - auto app_executable = af->executable(); - const int button_size = 24; - auto& button = quick_launch_bar.add<GUI::Button>(); - button.set_fixed_size(button_size, button_size); - button.set_button_style(Gfx::ButtonStyle::CoolBar); - button.set_icon(af->icon().bitmap_for_size(16)); - button.set_tooltip(af->name()); - button.on_click = [app_executable](auto) { - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - } else if (pid == 0) { - if (chdir(Core::StandardPaths::home_directory().characters()) < 0) { - perror("chdir"); - exit(1); - } - execl(app_executable.characters(), app_executable.characters(), nullptr); - perror("execl"); - ASSERT_NOT_REACHED(); - } else { - if (disown(pid) < 0) - perror("disown"); - } - }; - - if (!first) - total_width += quick_launch_bar.layout()->spacing(); - first = false; - total_width += button_size; - } - - quick_launch_bar.set_fixed_size(total_width, 24); -} - -void TaskbarWindow::on_screen_rect_change(const Gfx::IntRect& rect) -{ - Gfx::IntRect new_rect { rect.x(), rect.bottom() - taskbar_height() + 1, rect.width(), taskbar_height() }; - set_rect(new_rect); -} - -NonnullRefPtr<GUI::Button> TaskbarWindow::create_button(const WindowIdentifier& identifier) -{ - auto& button = main_widget()->add<TaskbarButton>(identifier); - button.set_min_size(20, 23); - button.set_max_size(140, 23); - button.set_text_alignment(Gfx::TextAlignment::CenterLeft); - button.set_icon(*m_default_icon); - return button; -} - -static bool should_include_window(GUI::WindowType window_type, bool is_frameless) -{ - return window_type == GUI::WindowType::Normal && !is_frameless; -} - -void TaskbarWindow::add_window_button(::Window& window, const WindowIdentifier& identifier) -{ - if (window.button()) - return; - window.set_button(create_button(identifier)); - auto* button = window.button(); - button->on_click = [window = &window, identifier, button](auto) { - // We need to look at the button's checked state here to figure - // out if the application is active or not. That's because this - // button's window may not actually be active when a modal window - // is displayed, in which case window->is_active() would return - // false because window is the modal window's owner (which is not - // active) - if (window->is_minimized() || !button->is_checked()) { - GUI::WindowServerConnection::the().post_message(Messages::WindowServer::WM_SetActiveWindow(identifier.client_id(), identifier.window_id())); - } else { - GUI::WindowServerConnection::the().post_message(Messages::WindowServer::WM_SetWindowMinimized(identifier.client_id(), identifier.window_id(), true)); - } - }; -} - -void TaskbarWindow::remove_window_button(::Window& window, bool was_removed) -{ - auto* button = window.button(); - if (!button) - return; - if (!was_removed) - static_cast<TaskbarButton*>(button)->clear_taskbar_rect(); - window.set_button(nullptr); - button->remove_from_parent(); -} - -void TaskbarWindow::update_window_button(::Window& window, bool show_as_active) -{ - auto* button = window.button(); - if (!button) - return; - button->set_text(window.title()); - button->set_checked(show_as_active); -} - -::Window* TaskbarWindow::find_window_owner(::Window& window) const -{ - if (!window.is_modal()) - return &window; - - ::Window* parent = nullptr; - auto* current_window = &window; - while (current_window) { - parent = WindowList::the().find_parent(*current_window); - if (!parent || !parent->is_modal()) - break; - current_window = parent; - } - return parent; -} - -void TaskbarWindow::wm_event(GUI::WMEvent& event) -{ - WindowIdentifier identifier { event.client_id(), event.window_id() }; - switch (event.type()) { - case GUI::Event::WM_WindowRemoved: { -#ifdef EVENT_DEBUG - auto& removed_event = static_cast<GUI::WMWindowRemovedEvent&>(event); - dbgln("WM_WindowRemoved: client_id={}, window_id={}", - removed_event.client_id(), - removed_event.window_id()); -#endif - if (auto* window = WindowList::the().window(identifier)) - remove_window_button(*window, true); - WindowList::the().remove_window(identifier); - update(); - break; - } - case GUI::Event::WM_WindowRectChanged: { -#ifdef EVENT_DEBUG - auto& changed_event = static_cast<GUI::WMWindowRectChangedEvent&>(event); - dbgln("WM_WindowRectChanged: client_id={}, window_id={}, rect={}", - changed_event.client_id(), - changed_event.window_id(), - changed_event.rect()); -#endif - break; - } - - case GUI::Event::WM_WindowIconBitmapChanged: { - auto& changed_event = static_cast<GUI::WMWindowIconBitmapChangedEvent&>(event); -#ifdef EVENT_DEBUG - dbgln("WM_WindowIconBitmapChanged: client_id={}, window_id={}, icon_buffer_id={}", - changed_event.client_id(), - changed_event.window_id(), - changed_event.icon_buffer_id()); -#endif - if (auto* window = WindowList::the().window(identifier)) { - auto buffer = SharedBuffer::create_from_shbuf_id(changed_event.icon_buffer_id()); - ASSERT(buffer); - if (window->button()) - window->button()->set_icon(Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *buffer, changed_event.icon_size())); - } - break; - } - - case GUI::Event::WM_WindowStateChanged: { - auto& changed_event = static_cast<GUI::WMWindowStateChangedEvent&>(event); -#ifdef EVENT_DEBUG - dbgln("WM_WindowStateChanged: client_id={}, window_id={}, title={}, rect={}, is_active={}, is_minimized={}", - changed_event.client_id(), - changed_event.window_id(), - changed_event.title(), - changed_event.rect(), - changed_event.is_active(), - changed_event.is_minimized()); -#endif - if (!should_include_window(changed_event.window_type(), changed_event.is_frameless())) - break; - auto& window = WindowList::the().ensure_window(identifier); - window.set_parent_identifier({ changed_event.parent_client_id(), changed_event.parent_window_id() }); - if (!window.is_modal()) - add_window_button(window, identifier); - else - remove_window_button(window, false); - window.set_title(changed_event.title()); - window.set_rect(changed_event.rect()); - window.set_modal(changed_event.is_modal()); - window.set_active(changed_event.is_active()); - window.set_minimized(changed_event.is_minimized()); - window.set_progress(changed_event.progress()); - - auto* window_owner = find_window_owner(window); - if (window_owner == &window) { - update_window_button(window, window.is_active()); - } else if (window_owner) { - // check the window owner's button if the modal's window button - // would have been checked - ASSERT(window.is_modal()); - update_window_button(*window_owner, window.is_active()); - } - break; - } - default: - break; - } -} diff --git a/Services/Taskbar/TaskbarWindow.h b/Services/Taskbar/TaskbarWindow.h deleted file mode 100644 index be55a00daf..0000000000 --- a/Services/Taskbar/TaskbarWindow.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "WindowList.h" -#include <LibGUI/Widget.h> -#include <LibGUI/Window.h> - -class TaskbarWindow final : public GUI::Window { - C_OBJECT(TaskbarWindow) -public: - TaskbarWindow(); - virtual ~TaskbarWindow() override; - - int taskbar_height() const { return 28; } - -private: - void create_quick_launch_bar(); - void on_screen_rect_change(const Gfx::IntRect&); - NonnullRefPtr<GUI::Button> create_button(const WindowIdentifier&); - void add_window_button(::Window&, const WindowIdentifier&); - void remove_window_button(::Window&, bool); - void update_window_button(::Window&, bool); - ::Window* find_window_owner(::Window&) const; - - virtual void wm_event(GUI::WMEvent&) override; - - RefPtr<Gfx::Bitmap> m_default_icon; -}; diff --git a/Services/Taskbar/WindowIdentifier.h b/Services/Taskbar/WindowIdentifier.h deleted file mode 100644 index c6e996992c..0000000000 --- a/Services/Taskbar/WindowIdentifier.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Traits.h> - -class WindowIdentifier { -public: - WindowIdentifier() = default; - WindowIdentifier(int client_id, int window_id) - : m_client_id(client_id) - , m_window_id(window_id) - { - } - - int client_id() const { return m_client_id; } - int window_id() const { return m_window_id; } - - bool operator==(const WindowIdentifier& other) const - { - return m_client_id == other.m_client_id && m_window_id == other.m_window_id; - } - - bool is_valid() const - { - return m_client_id != -1 && m_window_id != -1; - } - -private: - int m_client_id { -1 }; - int m_window_id { -1 }; -}; - -namespace AK { -template<> -struct Traits<WindowIdentifier> : public GenericTraits<WindowIdentifier> { - static unsigned hash(const WindowIdentifier& w) { return pair_int_hash(w.client_id(), w.window_id()); } -}; -} diff --git a/Services/Taskbar/WindowList.cpp b/Services/Taskbar/WindowList.cpp deleted file mode 100644 index 84021be160..0000000000 --- a/Services/Taskbar/WindowList.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "WindowList.h" - -WindowList& WindowList::the() -{ - static WindowList* s_the; - if (!s_the) - s_the = new WindowList; - return *s_the; -} - -Window* WindowList::find_parent(const Window& window) -{ - if (!window.parent_identifier().is_valid()) - return nullptr; - for (auto& it : m_windows) { - auto& w = *it.value; - if (w.identifier() == window.parent_identifier()) - return &w; - } - return nullptr; -} - -Window* WindowList::window(const WindowIdentifier& identifier) -{ - auto it = m_windows.find(identifier); - if (it != m_windows.end()) - return it->value; - return nullptr; -} - -Window& WindowList::ensure_window(const WindowIdentifier& identifier) -{ - auto it = m_windows.find(identifier); - if (it != m_windows.end()) - return *it->value; - auto window = make<Window>(identifier); - auto& window_ref = *window; - m_windows.set(identifier, move(window)); - return window_ref; -} - -void WindowList::remove_window(const WindowIdentifier& identifier) -{ - m_windows.remove(identifier); -} diff --git a/Services/Taskbar/WindowList.h b/Services/Taskbar/WindowList.h deleted file mode 100644 index b86b509926..0000000000 --- a/Services/Taskbar/WindowList.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "WindowIdentifier.h" -#include <AK/HashMap.h> -#include <AK/String.h> -#include <LibGUI/Button.h> -#include <LibGfx/Rect.h> - -class Window { -public: - explicit Window(const WindowIdentifier& identifier) - : m_identifier(identifier) - { - } - - ~Window() - { - if (m_button) - m_button->remove_from_parent(); - } - - const WindowIdentifier& identifier() const { return m_identifier; } - - void set_parent_identifier(const WindowIdentifier& parent_identifier) { m_parent_identifier = parent_identifier; } - const WindowIdentifier& parent_identifier() const { return m_parent_identifier; } - - String title() const { return m_title; } - void set_title(const String& title) { m_title = title; } - - Gfx::IntRect rect() const { return m_rect; } - void set_rect(const Gfx::IntRect& rect) { m_rect = rect; } - - GUI::Button* button() { return m_button; } - void set_button(GUI::Button* button) { m_button = button; } - - void set_active(bool active) { m_active = active; } - bool is_active() const { return m_active; } - - void set_minimized(bool minimized) { m_minimized = minimized; } - bool is_minimized() const { return m_minimized; } - - void set_modal(bool modal) { m_modal = modal; } - bool is_modal() const { return m_modal; } - - void set_progress(int progress) - { - if (m_progress == progress) - return; - m_progress = progress; - if (m_button) - m_button->update(); - } - - int progress() const { return m_progress; } - - const Gfx::Bitmap* icon() const { return m_icon.ptr(); } - -private: - WindowIdentifier m_identifier; - WindowIdentifier m_parent_identifier; - String m_title; - Gfx::IntRect m_rect; - RefPtr<GUI::Button> m_button; - RefPtr<Gfx::Bitmap> m_icon; - bool m_active { false }; - bool m_minimized { false }; - bool m_modal { false }; - int m_progress { -1 }; -}; - -class WindowList { -public: - static WindowList& the(); - - template<typename Callback> - void for_each_window(Callback callback) - { - for (auto& it : m_windows) - callback(*it.value); - } - - Window* find_parent(const Window&); - Window* window(const WindowIdentifier&); - Window& ensure_window(const WindowIdentifier&); - void remove_window(const WindowIdentifier&); - -private: - HashMap<WindowIdentifier, NonnullOwnPtr<Window>> m_windows; -}; diff --git a/Services/Taskbar/main.cpp b/Services/Taskbar/main.cpp deleted file mode 100644 index 3917285497..0000000000 --- a/Services/Taskbar/main.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "TaskbarWindow.h" -#include <LibCore/EventLoop.h> -#include <LibGUI/Application.h> -#include <signal.h> -#include <stdio.h> -#include <sys/wait.h> - -int main(int argc, char** argv) -{ - if (pledge("stdio shared_buffer accept proc exec rpath unix cpath fattr sigaction", nullptr) < 0) { - perror("pledge"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - app->event_loop().register_signal(SIGCHLD, [](int) { - // Wait all available children - while (waitpid(-1, nullptr, WNOHANG) > 0) - ; - }); - - if (pledge("stdio shared_buffer accept proc exec rpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - TaskbarWindow window; - window.show(); - - return app->exec(); -} diff --git a/Services/TelnetServer/CMakeLists.txt b/Services/TelnetServer/CMakeLists.txt deleted file mode 100644 index e19ae8bac6..0000000000 --- a/Services/TelnetServer/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - Client.cpp - main.cpp - Parser.cpp -) - -serenity_bin(TelnetServer) -target_link_libraries(TelnetServer LibCore) diff --git a/Services/TelnetServer/Client.cpp b/Services/TelnetServer/Client.cpp deleted file mode 100644 index 5e1accf9d1..0000000000 --- a/Services/TelnetServer/Client.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Client.h" -#include <AK/ByteBuffer.h> -#include <AK/MemoryStream.h> -#include <AK/String.h> -#include <AK/StringBuilder.h> -#include <AK/StringView.h> -#include <AK/Types.h> -#include <LibCore/Notifier.h> -#include <LibCore/TCPSocket.h> -#include <stdio.h> -#include <unistd.h> - -Client::Client(int id, RefPtr<Core::TCPSocket> socket, int ptm_fd) - : m_id(id) - , m_socket(move(socket)) - , m_ptm_fd(ptm_fd) - , m_ptm_notifier(Core::Notifier::construct(ptm_fd, Core::Notifier::Read)) -{ - m_socket->on_ready_to_read = [this] { drain_socket(); }; - m_ptm_notifier->on_ready_to_read = [this] { drain_pty(); }; - m_parser.on_command = [this](const Command& command) { handle_command(command); }; - m_parser.on_data = [this](const StringView& data) { handle_data(data); }; - m_parser.on_error = [this]() { handle_error(); }; - send_commands({ - { CMD_WILL, SUB_SUPPRESS_GO_AHEAD }, - { CMD_WILL, SUB_ECHO }, - { CMD_DO, SUB_SUPPRESS_GO_AHEAD }, - { CMD_DONT, SUB_ECHO }, - }); -} - -void Client::drain_socket() -{ - NonnullRefPtr<Client> protect(*this); - while (m_socket->can_read()) { - auto buf = m_socket->read(1024); - - m_parser.write(buf); - - if (m_socket->eof()) { - quit(); - break; - } - } -} - -void Client::drain_pty() -{ - u8 buffer[BUFSIZ]; - ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer)); - if (nread < 0) { - perror("read(ptm)"); - quit(); - return; - } - if (nread == 0) { - quit(); - return; - } - send_data(StringView(buffer, (size_t)nread)); -} - -void Client::handle_data(const StringView& data) -{ - write(m_ptm_fd, data.characters_without_null_termination(), data.length()); -} - -void Client::handle_command(const Command& command) -{ - switch (command.command) { - case CMD_DO: - // no response - we've already advertised our options, and none of - // them can be disabled (or re-enabled) after connecting. - break; - case CMD_DONT: - // no response - we only "support" two options (echo and suppress - // go-ahead), and both of them are always enabled. - break; - case CMD_WILL: - switch (command.subcommand) { - case SUB_ECHO: - // we always want to be the ones in control of the output. tell - // the client to disable local echo. - send_command({ CMD_DONT, SUB_ECHO }); - break; - case SUB_SUPPRESS_GO_AHEAD: - send_command({ CMD_DO, SUB_SUPPRESS_GO_AHEAD }); - break; - default: - // don't respond to unknown commands - break; - } - break; - case CMD_WONT: - // no response - we don't care about anything the client says they - // won't do. - break; - } -} - -void Client::handle_error() -{ - quit(); -} - -void Client::send_data(StringView data) -{ - bool fast = true; - for (size_t i = 0; i < data.length(); i++) { - u8 c = data[i]; - if (c == '\n' || c == 0xff) - fast = false; - } - - if (fast) { - m_socket->write(data); - return; - } - - StringBuilder builder; - for (size_t i = 0; i < data.length(); i++) { - u8 c = data[i]; - - switch (c) { - case '\n': - builder.append("\r\n"); - break; - case IAC: - builder.append("\xff\xff"); - break; - default: - builder.append(c); - break; - } - } - - m_socket->write(builder.to_string()); -} - -void Client::send_command(Command command) -{ - send_commands({ command }); -} - -void Client::send_commands(Vector<Command> commands) -{ - auto buffer = ByteBuffer::create_uninitialized(commands.size() * 3); - OutputMemoryStream stream { buffer }; - - for (auto& command : commands) - stream << (u8)IAC << command.command << command.subcommand; - - ASSERT(stream.is_end()); - m_socket->write(buffer.data(), buffer.size()); -} - -void Client::quit() -{ - m_ptm_notifier->set_enabled(false); - close(m_ptm_fd); - m_socket->close(); - if (on_exit) - on_exit(); -} diff --git a/Services/TelnetServer/Client.h b/Services/TelnetServer/Client.h deleted file mode 100644 index fe345d38e3..0000000000 --- a/Services/TelnetServer/Client.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/String.h> -#include <AK/StringView.h> -#include <AK/Types.h> -#include <LibCore/Notifier.h> -#include <LibCore/TCPSocket.h> - -#include "Command.h" -#include "Parser.h" - -class Client : public RefCounted<Client> { -public: - static NonnullRefPtr<Client> create(int id, RefPtr<Core::TCPSocket> socket, int ptm_fd) - { - return adopt(*new Client(id, move(socket), ptm_fd)); - } - - Function<void()> on_exit; - -protected: - Client(int id, RefPtr<Core::TCPSocket> socket, int ptm_fd); - - void drain_socket(); - void drain_pty(); - void handle_data(const StringView&); - void handle_command(const Command& command); - void handle_error(); - void send_data(StringView str); - void send_command(Command command); - void send_commands(Vector<Command> commands); - void quit(); - -private: - // client id - int m_id { 0 }; - // client resources - RefPtr<Core::TCPSocket> m_socket; - Parser m_parser; - // pty resources - int m_ptm_fd { -1 }; - RefPtr<Core::Notifier> m_ptm_notifier; -}; diff --git a/Services/TelnetServer/Command.h b/Services/TelnetServer/Command.h deleted file mode 100644 index 25afb1fe22..0000000000 --- a/Services/TelnetServer/Command.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/String.h> -#include <AK/StringBuilder.h> -#include <AK/Types.h> - -#define CMD_WILL 0xfb -#define CMD_WONT 0xfc -#define CMD_DO 0xfd -#define CMD_DONT 0xfe -#define SUB_ECHO 0x01 -#define SUB_SUPPRESS_GO_AHEAD 0x03 - -struct Command { - u8 command; - u8 subcommand; - - String to_string() const - { - StringBuilder builder; - - switch (command) { - case CMD_WILL: - builder.append("WILL"); - break; - case CMD_WONT: - builder.append("WONT"); - break; - case CMD_DO: - builder.append("DO"); - break; - case CMD_DONT: - builder.append("DONT"); - break; - default: - builder.append(String::format("UNKNOWN<%02x>", command)); - break; - } - - builder.append(" "); - - switch (subcommand) { - case SUB_ECHO: - builder.append("ECHO"); - break; - case SUB_SUPPRESS_GO_AHEAD: - builder.append("SUPPRESS_GO_AHEAD"); - break; - default: - builder.append(String::format("UNKNOWN<%02x>", subcommand)); - break; - } - - return builder.to_string(); - }; -}; diff --git a/Services/TelnetServer/Parser.cpp b/Services/TelnetServer/Parser.cpp deleted file mode 100644 index 9fc1aabcd1..0000000000 --- a/Services/TelnetServer/Parser.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Function.h> -#include <AK/String.h> -#include <AK/Types.h> - -#include "Parser.h" - -void Parser::write(const StringView& data) -{ - for (size_t i = 0; i < data.length(); i++) { - u8 ch = data[i]; - - switch (m_state) { - case State::Free: - switch (ch) { - case IAC: - m_state = State::ReadCommand; - break; - case '\r': - if (on_data) - on_data("\n"); - break; - default: - if (on_data) - on_data(StringView(&ch, 1)); - break; - } - break; - case State::ReadCommand: - switch (ch) { - case IAC: { - m_state = State::Free; - if (on_data) - on_data("\xff"); - break; - } - case CMD_WILL: - case CMD_WONT: - case CMD_DO: - case CMD_DONT: - m_command = ch; - m_state = State::ReadSubcommand; - break; - default: - m_state = State::Error; - if (on_error) - on_error(); - break; - } - break; - case State::ReadSubcommand: { - auto command = m_command; - m_command = 0; - m_state = State::Free; - if (on_command) - on_command({ command, ch }); - break; - } - case State::Error: - // ignore everything - break; - } - } -} diff --git a/Services/TelnetServer/Parser.h b/Services/TelnetServer/Parser.h deleted file mode 100644 index 64688881df..0000000000 --- a/Services/TelnetServer/Parser.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Function.h> -#include <AK/String.h> -#include <AK/StringView.h> -#include <AK/Types.h> - -#include "Command.h" - -#define IAC 0xff - -class Parser { -public: - Function<void(const Command&)> on_command; - Function<void(const StringView&)> on_data; - Function<void()> on_error; - - void write(const StringView&); - -protected: - enum State { - Free, - ReadCommand, - ReadSubcommand, - Error, - }; - - void write(const String& str); - -private: - State m_state { State::Free }; - u8 m_command { 0 }; -}; diff --git a/Services/TelnetServer/main.cpp b/Services/TelnetServer/main.cpp deleted file mode 100644 index dc5ddf1a04..0000000000 --- a/Services/TelnetServer/main.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Client.h" -#include <AK/ByteBuffer.h> -#include <AK/HashMap.h> -#include <AK/IPv4Address.h> -#include <AK/String.h> -#include <AK/StringBuilder.h> -#include <AK/Types.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/EventLoop.h> -#include <LibCore/TCPServer.h> -#include <LibCore/TCPSocket.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/ioctl.h> - -static void run_command(int ptm_fd, String command) -{ - pid_t pid = fork(); - if (pid == 0) { - const char* tty_name = ptsname(ptm_fd); - if (!tty_name) { - perror("ptsname"); - exit(1); - } - close(ptm_fd); - int pts_fd = open(tty_name, O_RDWR); - if (pts_fd < 0) { - perror("open"); - exit(1); - } - - // NOTE: It's okay if this fails. - [[maybe_unused]] auto rc = ioctl(0, TIOCNOTTY); - - close(0); - close(1); - close(2); - - rc = dup2(pts_fd, 0); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = dup2(pts_fd, 1); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = dup2(pts_fd, 2); - if (rc < 0) { - perror("dup2"); - exit(1); - } - rc = close(pts_fd); - if (rc < 0) { - perror("close"); - exit(1); - } - rc = ioctl(0, TIOCSCTTY); - if (rc < 0) { - perror("ioctl(TIOCSCTTY)"); - exit(1); - } - const char* args[4] = { "/bin/Shell", nullptr, nullptr, nullptr }; - if (!command.is_empty()) { - args[1] = "-c"; - args[2] = command.characters(); - } - const char* envs[] = { "TERM=xterm", "PATH=/bin:/usr/bin:/usr/local/bin", nullptr }; - rc = execve("/bin/Shell", const_cast<char**>(args), const_cast<char**>(envs)); - if (rc < 0) { - perror("execve"); - exit(1); - } - ASSERT_NOT_REACHED(); - } -} - -int main(int argc, char** argv) -{ - int port = 23; - const char* command = ""; - - Core::ArgsParser args_parser; - args_parser.add_option(port, "Port to listen on", nullptr, 'p', "port"); - args_parser.add_option(command, "Program to run on connection", nullptr, 'c', "command"); - args_parser.parse(argc, argv); - - if ((u16)port != port) { - warnln("Invalid port number: {}", port); - exit(1); - } - - Core::EventLoop event_loop; - auto server = Core::TCPServer::construct(); - - if (!server->listen({}, port)) { - warnln("Listening on 0.0.0.0:{} failed", port); - exit(1); - } - - HashMap<int, NonnullRefPtr<Client>> clients; - int next_id = 0; - - server->on_ready_to_accept = [&next_id, &clients, &server, command] { - int id = next_id++; - - auto client_socket = server->accept(); - if (!client_socket) { - perror("accept"); - return; - } - - int ptm_fd = posix_openpt(O_RDWR); - if (ptm_fd < 0) { - perror("posix_openpt"); - client_socket->close(); - return; - } - if (grantpt(ptm_fd) < 0) { - perror("grantpt"); - client_socket->close(); - return; - } - if (unlockpt(ptm_fd) < 0) { - perror("unlockpt"); - client_socket->close(); - return; - } - - run_command(ptm_fd, command); - - auto client = Client::create(id, move(client_socket), ptm_fd); - client->on_exit = [&clients, id] { clients.remove(id); }; - clients.set(id, client); - }; - - int rc = event_loop.exec(); - if (rc != 0) { - fprintf(stderr, "event loop exited badly; rc=%d", rc); - exit(1); - } - - return 0; -} diff --git a/Services/WebContent/CMakeLists.txt b/Services/WebContent/CMakeLists.txt deleted file mode 100644 index a7e367ff94..0000000000 --- a/Services/WebContent/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -compile_ipc(WebContentServer.ipc WebContentServerEndpoint.h) -compile_ipc(WebContentClient.ipc WebContentClientEndpoint.h) - -set(SOURCES - ClientConnection.cpp - main.cpp - PageHost.cpp - WebContentServerEndpoint.h - WebContentClientEndpoint.h -) - -serenity_bin(WebContent) -target_link_libraries(WebContent LibCore LibIPC LibGfx LibWeb) diff --git a/Services/WebContent/ClientConnection.cpp b/Services/WebContent/ClientConnection.cpp deleted file mode 100644 index 46b38f440b..0000000000 --- a/Services/WebContent/ClientConnection.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Badge.h> -#include <AK/SharedBuffer.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/SystemTheme.h> -#include <WebContent/ClientConnection.h> -#include <WebContent/PageHost.h> -#include <WebContent/WebContentClientEndpoint.h> - -namespace WebContent { - -static HashMap<int, RefPtr<ClientConnection>> s_connections; - -ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id) - : IPC::ClientConnection<WebContentClientEndpoint, WebContentServerEndpoint>(*this, move(socket), client_id) - , m_page_host(PageHost::create(*this)) -{ - s_connections.set(client_id, *this); - m_paint_flush_timer = Core::Timer::create_single_shot(0, [this] { flush_pending_paint_requests(); }); -} - -ClientConnection::~ClientConnection() -{ -} - -void ClientConnection::die() -{ - s_connections.remove(client_id()); - if (s_connections.is_empty()) - Core::EventLoop::current().quit(0); -} - -Web::Page& ClientConnection::page() -{ - return m_page_host->page(); -} - -const Web::Page& ClientConnection::page() const -{ - return m_page_host->page(); -} - -OwnPtr<Messages::WebContentServer::GreetResponse> ClientConnection::handle(const Messages::WebContentServer::Greet& message) -{ - set_client_pid(message.client_pid()); - return make<Messages::WebContentServer::GreetResponse>(client_id(), getpid()); -} - -void ClientConnection::handle(const Messages::WebContentServer::UpdateSystemTheme& message) -{ - auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.shbuf_id()); - if (!shared_buffer) { - dbgln("WebContentServer::UpdateSystemTheme: SharedBuffer already gone! Ignoring :^)"); - return; - } - Gfx::set_system_theme(*shared_buffer); - auto impl = Gfx::PaletteImpl::create_with_shared_buffer(*shared_buffer); - m_page_host->set_palette_impl(*impl); -} - -void ClientConnection::handle(const Messages::WebContentServer::LoadURL& message) -{ -#ifdef DEBUG_SPAM - dbg() << "handle: WebContentServer::LoadURL: url=" << message.url(); -#endif - page().load(message.url()); -} - -void ClientConnection::handle(const Messages::WebContentServer::LoadHTML& message) -{ -#ifdef DEBUG_SPAM - dbg() << "handle: WebContentServer::LoadHTML: html=" << message.html() << ", url=" << message.url(); -#endif - page().load_html(message.html(), message.url()); -} - -void ClientConnection::handle(const Messages::WebContentServer::SetViewportRect& message) -{ -#ifdef DEBUG_SPAM - dbg() << "handle: WebContentServer::SetViewportRect: rect=" << message.rect(); -#endif - m_page_host->set_viewport_rect(message.rect()); -} - -void ClientConnection::handle(const Messages::WebContentServer::Paint& message) -{ -#ifdef DEBUG_SPAM - dbg() << "handle: WebContentServer::Paint: content_rect=" << message.content_rect() << ", shbuf_id=" << message.shbuf_id(); -#endif - - for (auto& pending_paint : m_pending_paint_requests) { - if (pending_paint.bitmap->shbuf_id() == message.shbuf_id()) { - pending_paint.content_rect = message.content_rect(); - return; - } - } - - auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.shbuf_id()); - if (!shared_buffer) { -#ifdef DEBUG_SPAM - dbgln("WebContentServer::Paint: SharedBuffer already gone! Ignoring :^)"); -#endif - return; - } - auto shared_bitmap = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGB32, shared_buffer.release_nonnull(), message.content_rect().size()); - if (!shared_bitmap) { - did_misbehave("WebContentServer::Paint: Cannot create Gfx::Bitmap wrapper around SharedBuffer"); - return; - } - - m_pending_paint_requests.append({ message.content_rect(), shared_bitmap.release_nonnull() }); - m_paint_flush_timer->start(); -} - -void ClientConnection::flush_pending_paint_requests() -{ - for (auto& pending_paint : m_pending_paint_requests) { - m_page_host->paint(pending_paint.content_rect, *pending_paint.bitmap); - post_message(Messages::WebContentClient::DidPaint(pending_paint.content_rect, pending_paint.bitmap->shbuf_id())); - } - m_pending_paint_requests.clear(); -} - -void ClientConnection::handle(const Messages::WebContentServer::MouseDown& message) -{ - page().handle_mousedown(message.position(), message.button(), message.modifiers()); -} - -void ClientConnection::handle(const Messages::WebContentServer::MouseMove& message) -{ - page().handle_mousemove(message.position(), message.buttons(), message.modifiers()); -} - -void ClientConnection::handle(const Messages::WebContentServer::MouseUp& message) -{ - page().handle_mouseup(message.position(), message.button(), message.modifiers()); -} - -void ClientConnection::handle(const Messages::WebContentServer::KeyDown& message) -{ - page().handle_keydown((KeyCode)message.key(), message.modifiers(), message.code_point()); -} - -} diff --git a/Services/WebContent/ClientConnection.h b/Services/WebContent/ClientConnection.h deleted file mode 100644 index 42e2ab8313..0000000000 --- a/Services/WebContent/ClientConnection.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/HashMap.h> -#include <LibIPC/ClientConnection.h> -#include <LibWeb/Forward.h> -#include <WebContent/Forward.h> -#include <WebContent/WebContentClientEndpoint.h> -#include <WebContent/WebContentServerEndpoint.h> - -namespace WebContent { - -class ClientConnection final - : public IPC::ClientConnection<WebContentClientEndpoint, WebContentServerEndpoint> - , public WebContentServerEndpoint { - C_OBJECT(ClientConnection); - -public: - explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); - ~ClientConnection() override; - - virtual void die() override; - -private: - Web::Page& page(); - const Web::Page& page() const; - - virtual OwnPtr<Messages::WebContentServer::GreetResponse> handle(const Messages::WebContentServer::Greet&) override; - virtual void handle(const Messages::WebContentServer::UpdateSystemTheme&) override; - virtual void handle(const Messages::WebContentServer::LoadURL&) override; - virtual void handle(const Messages::WebContentServer::LoadHTML&) override; - virtual void handle(const Messages::WebContentServer::Paint&) override; - virtual void handle(const Messages::WebContentServer::SetViewportRect&) override; - virtual void handle(const Messages::WebContentServer::MouseDown&) override; - virtual void handle(const Messages::WebContentServer::MouseMove&) override; - virtual void handle(const Messages::WebContentServer::MouseUp&) override; - virtual void handle(const Messages::WebContentServer::KeyDown&) override; - - void flush_pending_paint_requests(); - - NonnullOwnPtr<PageHost> m_page_host; - - struct PaintRequest { - Gfx::IntRect content_rect; - NonnullRefPtr<Gfx::Bitmap> bitmap; - }; - Vector<PaintRequest> m_pending_paint_requests; - RefPtr<Core::Timer> m_paint_flush_timer; -}; - -} diff --git a/Services/WebContent/Documentation.txt b/Services/WebContent/Documentation.txt deleted file mode 100644 index e82190106a..0000000000 --- a/Services/WebContent/Documentation.txt +++ /dev/null @@ -1,27 +0,0 @@ -===================== -Multi-process model: -===================== - -Server Client - -WebContent GUI process (OutOfProcessWebView embedder) - - OutOfProcessWebView (this is a GUI::Widget) -WebContent::ClientConnection <---> WebContentClient - WebContent::PageHost (Web::PageClient) - Web::Page - Web::Frame - Web::Document - .. - - -===================== -Single process model: -===================== - -Web::InProcessWebView (this is a GUI::Widget, and also a Web::PageClient) - Web::Page - Web::Frame - Web::Document - .. - diff --git a/Services/WebContent/Forward.h b/Services/WebContent/Forward.h deleted file mode 100644 index e43ca83960..0000000000 --- a/Services/WebContent/Forward.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -namespace WebContent { - -class ClientConnection; -class PageHost; - -} diff --git a/Services/WebContent/PageHost.cpp b/Services/WebContent/PageHost.cpp deleted file mode 100644 index 11641235eb..0000000000 --- a/Services/WebContent/PageHost.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "PageHost.h" -#include "ClientConnection.h" -#include <AK/SharedBuffer.h> -#include <LibGfx/Painter.h> -#include <LibGfx/SystemTheme.h> -#include <LibWeb/Layout/InitialContainingBlockBox.h> -#include <LibWeb/Page/Frame.h> -#include <WebContent/WebContentClientEndpoint.h> - -namespace WebContent { - -PageHost::PageHost(ClientConnection& client) - : m_client(client) - , m_page(make<Web::Page>(*this)) -{ - setup_palette(); -} - -PageHost::~PageHost() -{ -} - -void PageHost::setup_palette() -{ - // FIXME: Get the proper palette from our peer somehow - auto buffer = SharedBuffer::create_with_size(sizeof(Gfx::SystemTheme)); - auto* theme = buffer->data<Gfx::SystemTheme>(); - theme->color[(int)Gfx::ColorRole::Window] = Color::Magenta; - theme->color[(int)Gfx::ColorRole::WindowText] = Color::Cyan; - m_palette_impl = Gfx::PaletteImpl::create_with_shared_buffer(*buffer); -} - -Gfx::Palette PageHost::palette() const -{ - return Gfx::Palette(*m_palette_impl); -} - -void PageHost::set_palette_impl(const Gfx::PaletteImpl& impl) -{ - m_palette_impl = impl; -} - -Web::Layout::InitialContainingBlockBox* PageHost::layout_root() -{ - auto* document = page().main_frame().document(); - if (!document) - return nullptr; - return document->layout_node(); -} - -void PageHost::paint(const Gfx::IntRect& content_rect, Gfx::Bitmap& target) -{ - Gfx::Painter painter(target); - Gfx::IntRect bitmap_rect { {}, content_rect.size() }; - - auto* layout_root = this->layout_root(); - if (!layout_root) { - painter.fill_rect(bitmap_rect, Color::White); - return; - } - - painter.fill_rect(bitmap_rect, layout_root->document().background_color(palette())); - - if (auto background_bitmap = layout_root->document().background_image()) { - painter.draw_tiled_bitmap(bitmap_rect, *background_bitmap); - } - - painter.translate(-content_rect.x(), -content_rect.y()); - - Web::PaintContext context(painter, palette(), Gfx::IntPoint()); - context.set_viewport_rect(content_rect); - layout_root->paint_all_phases(context); -} - -void PageHost::set_viewport_rect(const Gfx::IntRect& rect) -{ - page().main_frame().set_size(rect.size()); - if (page().main_frame().document()) - page().main_frame().document()->update_layout(); - page().main_frame().set_viewport_scroll_offset(rect.location()); -} - -void PageHost::page_did_invalidate(const Gfx::IntRect& content_rect) -{ - m_client.post_message(Messages::WebContentClient::DidInvalidateContentRect(content_rect)); -} - -void PageHost::page_did_change_selection() -{ - m_client.post_message(Messages::WebContentClient::DidChangeSelection()); -} - -void PageHost::page_did_layout() -{ - auto* layout_root = this->layout_root(); - ASSERT(layout_root); - auto content_size = enclosing_int_rect(layout_root->absolute_rect()).size(); - m_client.post_message(Messages::WebContentClient::DidLayout(content_size)); -} - -void PageHost::page_did_change_title(const String& title) -{ - m_client.post_message(Messages::WebContentClient::DidChangeTitle(title)); -} - -void PageHost::page_did_request_scroll_into_view(const Gfx::IntRect& rect) -{ - m_client.post_message(Messages::WebContentClient::DidRequestScrollIntoView(rect)); -} - -void PageHost::page_did_hover_link(const URL& url) -{ - m_client.post_message(Messages::WebContentClient::DidHoverLink(url)); -} - -void PageHost::page_did_unhover_link() -{ - m_client.post_message(Messages::WebContentClient::DidUnhoverLink()); -} - -void PageHost::page_did_click_link(const URL& url, const String& target, unsigned modifiers) -{ - m_client.post_message(Messages::WebContentClient::DidClickLink(url, target, modifiers)); -} - -void PageHost::page_did_middle_click_link(const URL& url, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) -{ - m_client.post_message(Messages::WebContentClient::DidMiddleClickLink(url, target, modifiers)); -} - -void PageHost::page_did_start_loading(const URL& url) -{ - m_client.post_message(Messages::WebContentClient::DidStartLoading(url)); -} - -void PageHost::page_did_finish_loading(const URL& url) -{ - m_client.post_message(Messages::WebContentClient::DidFinishLoading(url)); -} - -void PageHost::page_did_request_context_menu(const Gfx::IntPoint& content_position) -{ - m_client.post_message(Messages::WebContentClient::DidRequestContextMenu(content_position)); -} - -void PageHost::page_did_request_link_context_menu(const Gfx::IntPoint& content_position, const URL& url, const String& target, unsigned modifiers) -{ - m_client.post_message(Messages::WebContentClient::DidRequestLinkContextMenu(content_position, url, target, modifiers)); -} - -void PageHost::page_did_request_alert(const String& message) -{ - m_client.send_sync<Messages::WebContentClient::DidRequestAlert>(message); -} - -} diff --git a/Services/WebContent/PageHost.h b/Services/WebContent/PageHost.h deleted file mode 100644 index 0ee9e393d9..0000000000 --- a/Services/WebContent/PageHost.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <LibWeb/Page/Page.h> - -namespace WebContent { - -class ClientConnection; - -class PageHost final : public Web::PageClient { - AK_MAKE_NONCOPYABLE(PageHost); - AK_MAKE_NONMOVABLE(PageHost); - -public: - static NonnullOwnPtr<PageHost> create(ClientConnection& client) { return adopt_own(*new PageHost(client)); } - virtual ~PageHost(); - - Web::Page& page() { return *m_page; } - const Web::Page& page() const { return *m_page; } - - void paint(const Gfx::IntRect& content_rect, Gfx::Bitmap&); - - void set_palette_impl(const Gfx::PaletteImpl&); - void set_viewport_rect(const Gfx::IntRect&); - -private: - // ^PageClient - virtual Gfx::Palette palette() const override; - virtual void page_did_invalidate(const Gfx::IntRect&) override; - virtual void page_did_change_selection() override; - virtual void page_did_layout() override; - virtual void page_did_change_title(const String&) override; - virtual void page_did_request_scroll_into_view(const Gfx::IntRect&) override; - virtual void page_did_hover_link(const URL&) override; - virtual void page_did_unhover_link() override; - virtual void page_did_click_link(const URL&, const String& target, unsigned modifiers) override; - virtual void page_did_middle_click_link(const URL&, const String& target, unsigned modifiers) override; - virtual void page_did_request_context_menu(const Gfx::IntPoint&) override; - virtual void page_did_request_link_context_menu(const Gfx::IntPoint&, const URL&, const String& target, unsigned modifiers) override; - virtual void page_did_start_loading(const URL&) override; - virtual void page_did_finish_loading(const URL&) override; - virtual void page_did_request_alert(const String&) override; - - explicit PageHost(ClientConnection&); - - Web::Layout::InitialContainingBlockBox* layout_root(); - void setup_palette(); - - ClientConnection& m_client; - NonnullOwnPtr<Web::Page> m_page; - RefPtr<Gfx::PaletteImpl> m_palette_impl; -}; - -} diff --git a/Services/WebContent/WebContentClient.ipc b/Services/WebContent/WebContentClient.ipc deleted file mode 100644 index 10008a765c..0000000000 --- a/Services/WebContent/WebContentClient.ipc +++ /dev/null @@ -1,18 +0,0 @@ -endpoint WebContentClient = 90 -{ - DidStartLoading(URL url) =| - DidFinishLoading(URL url) =| - DidPaint(Gfx::IntRect content_rect, i32 shbuf_id) =| - DidInvalidateContentRect(Gfx::IntRect content_rect) =| - DidChangeSelection() =| - DidLayout(Gfx::IntSize content_size) =| - DidChangeTitle(String title) =| - DidRequestScrollIntoView(Gfx::IntRect rect) =| - DidHoverLink(URL url) =| - DidUnhoverLink() =| - DidClickLink(URL url, String target, unsigned modifiers) =| - DidMiddleClickLink(URL url, String target, unsigned modifiers) =| - DidRequestContextMenu(Gfx::IntPoint content_position) =| - DidRequestLinkContextMenu(Gfx::IntPoint content_position, URL url, String target, unsigned modifiers) =| - DidRequestAlert(String message) => () -} diff --git a/Services/WebContent/WebContentServer.ipc b/Services/WebContent/WebContentServer.ipc deleted file mode 100644 index 143650dab7..0000000000 --- a/Services/WebContent/WebContentServer.ipc +++ /dev/null @@ -1,18 +0,0 @@ -endpoint WebContentServer = 89 -{ - Greet(i32 client_pid) => (i32 client_id, i32 server_pid) - - UpdateSystemTheme(i32 shbuf_id) =| - - LoadURL(URL url) =| - LoadHTML(String html, URL url) =| - - Paint(Gfx::IntRect content_rect, i32 shbuf_id) =| - SetViewportRect(Gfx::IntRect rect) =| - - MouseDown(Gfx::IntPoint position, unsigned button, unsigned buttons, unsigned modifiers) =| - MouseMove(Gfx::IntPoint position, unsigned button, unsigned buttons, unsigned modifiers) =| - MouseUp(Gfx::IntPoint position, unsigned button, unsigned buttons, unsigned modifiers) =| - - KeyDown(i32 key, unsigned modifiers, u32 code_point) =| -} diff --git a/Services/WebContent/main.cpp b/Services/WebContent/main.cpp deleted file mode 100644 index 5367f2cce3..0000000000 --- a/Services/WebContent/main.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibCore/EventLoop.h> -#include <LibCore/LocalServer.h> -#include <LibIPC/ClientConnection.h> -#include <WebContent/ClientConnection.h> - -int main(int, char**) -{ - Core::EventLoop event_loop; - if (pledge("stdio shared_buffer accept unix rpath recvfd", nullptr) < 0) { - perror("pledge"); - return 1; - } - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - if (unveil("/tmp/portal/protocol", "rw") < 0) { - perror("unveil"); - return 1; - } - if (unveil("/tmp/portal/image", "rw") < 0) { - perror("unveil"); - return 1; - } - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server(); - ASSERT(socket); - IPC::new_client_connection<WebContent::ClientConnection>(socket.release_nonnull(), 1); - return event_loop.exec(); -} diff --git a/Services/WebServer/CMakeLists.txt b/Services/WebServer/CMakeLists.txt deleted file mode 100644 index 34b1f037c9..0000000000 --- a/Services/WebServer/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SOURCES - Client.cpp - main.cpp -) - -serenity_bin(WebServer) -target_link_libraries(WebServer LibCore LibHTTP) diff --git a/Services/WebServer/Client.cpp b/Services/WebServer/Client.cpp deleted file mode 100644 index c3840bd118..0000000000 --- a/Services/WebServer/Client.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Client.h" -#include <AK/Base64.h> -#include <AK/LexicalPath.h> -#include <AK/MappedFile.h> -#include <AK/StringBuilder.h> -#include <AK/URLParser.h> -#include <LibCore/DateTime.h> -#include <LibCore/DirIterator.h> -#include <LibCore/File.h> -#include <LibCore/MimeData.h> -#include <LibHTTP/HttpRequest.h> -#include <stdio.h> -#include <sys/stat.h> -#include <time.h> -#include <unistd.h> - -namespace WebServer { - -Client::Client(NonnullRefPtr<Core::TCPSocket> socket, const String& root, Core::Object* parent) - : Core::Object(parent) - , m_socket(socket) - , m_root_path(root) -{ -} - -void Client::die() -{ - remove_from_parent(); -} - -void Client::start() -{ - m_socket->on_ready_to_read = [this] { - auto raw_request = m_socket->read_all(); - if (raw_request.is_null()) { - die(); - return; - } - - dbg() << "Got raw request: '" << String::copy(raw_request) << "'"; - - handle_request(raw_request.bytes()); - die(); - }; -} - -void Client::handle_request(ReadonlyBytes raw_request) -{ - auto request_or_error = HTTP::HttpRequest::from_raw_request(raw_request); - if (!request_or_error.has_value()) - return; - auto& request = request_or_error.value(); - - dbg() << "Got HTTP request: " << request.method_name() << " " << request.resource(); - for (auto& header : request.headers()) { - dbg() << " " << header.name << " => " << header.value; - } - - if (request.method() != HTTP::HttpRequest::Method::GET) { - send_error_response(403, "Forbidden!", request); - return; - } - - auto requested_path = LexicalPath::canonicalized_path(request.resource()); - dbg() << "Canonical requested path: '" << requested_path << "'"; - - StringBuilder path_builder; - path_builder.append(m_root_path); - path_builder.append('/'); - path_builder.append(requested_path); - auto real_path = path_builder.to_string(); - - if (Core::File::is_directory(real_path)) { - - if (!request.resource().ends_with("/")) { - StringBuilder red; - - red.append(requested_path); - red.append("/"); - - send_redirect(red.to_string(), request); - return; - } - - StringBuilder index_html_path_builder; - index_html_path_builder.append(real_path); - index_html_path_builder.append("/index.html"); - auto index_html_path = index_html_path_builder.to_string(); - if (!Core::File::exists(index_html_path)) { - handle_directory_listing(requested_path, real_path, request); - return; - } - real_path = index_html_path; - } - - auto file = Core::File::construct(real_path); - if (!file->open(Core::File::ReadOnly)) { - send_error_response(404, "Not found!", request); - return; - } - - send_response(file->read_all(), request, Core::guess_mime_type_based_on_filename(real_path)); -} - -void Client::send_response(StringView response, const HTTP::HttpRequest& request, const String& content_type) -{ - StringBuilder builder; - builder.append("HTTP/1.0 200 OK\r\n"); - builder.append("Server: WebServer (SerenityOS)\r\n"); - builder.append("Content-Type: "); - builder.append(content_type); - builder.append("\r\n"); - builder.append("\r\n"); - - m_socket->write(builder.to_string()); - m_socket->write(response); - - log_response(200, request); -} - -void Client::send_redirect(StringView redirect_path, const HTTP::HttpRequest& request) -{ - StringBuilder builder; - builder.append("HTTP/1.0 301 Moved Permanently\r\n"); - builder.append("Location: "); - builder.append(redirect_path); - builder.append("\r\n"); - builder.append("\r\n"); - - m_socket->write(builder.to_string()); - - log_response(301, request); -} - -static String folder_image_data() -{ - static String cache; - if (cache.is_empty()) { - auto file_or_error = MappedFile::map("/res/icons/16x16/filetype-folder.png"); - ASSERT(!file_or_error.is_error()); - cache = encode_base64(file_or_error.value()->bytes()); - } - return cache; -} - -static String file_image_data() -{ - static String cache; - if (cache.is_empty()) { - auto file_or_error = MappedFile::map("/res/icons/16x16/filetype-unknown.png"); - ASSERT(!file_or_error.is_error()); - cache = encode_base64(file_or_error.value()->bytes()); - } - return cache; -} - -void Client::handle_directory_listing(const String& requested_path, const String& real_path, const HTTP::HttpRequest& request) -{ - StringBuilder builder; - - builder.append("<!DOCTYPE html>\n"); - builder.append("<html>\n"); - builder.append("<head><title>Index of "); - builder.append(escape_html_entities(requested_path)); - builder.append("</title><style>\n"); - builder.append(".folder { width: 16px; height: 16px; background-image: url('data:image/png;base64,"); - builder.append(folder_image_data()); - builder.append("'); }\n"); - builder.append(".file { width: 16px; height: 16px; background-image: url('data:image/png;base64,"); - builder.append(file_image_data()); - builder.append("'); }\n"); - builder.append("</style></head><body>\n"); - builder.append("<h1>Index of "); - builder.append(escape_html_entities(requested_path)); - builder.append("</h1>\n"); - builder.append("<hr>\n"); - builder.append("<code><table>\n"); - - Core::DirIterator dt(real_path); - while (dt.has_next()) { - auto name = dt.next_path(); - - StringBuilder path_builder; - path_builder.append(real_path); - path_builder.append('/'); - path_builder.append(name); - struct stat st; - memset(&st, 0, sizeof(st)); - int rc = stat(path_builder.to_string().characters(), &st); - if (rc < 0) { - perror("stat"); - } - - bool is_directory = S_ISDIR(st.st_mode) || name.is_one_of(".", ".."); - - builder.append("<tr>"); - builder.appendf("<td><div class=\"%s\"></div></td>", is_directory ? "folder" : "file"); - builder.append("<td><a href=\""); - builder.append(urlencode(name)); - builder.append("\">"); - builder.append(escape_html_entities(name)); - builder.append("</a></td><td> </td>"); - - builder.appendf("<td>%10zd</td><td> </td>", st.st_size); - builder.append("<td>"); - builder.append(Core::DateTime::from_timestamp(st.st_mtime).to_string()); - builder.append("</td>"); - builder.append("</tr>\n"); - } - - builder.append("</table></code>\n"); - builder.append("<hr>\n"); - builder.append("<i>Generated by WebServer (SerenityOS)</i>\n"); - builder.append("</body>\n"); - builder.append("</html>\n"); - - send_response(builder.to_string(), request, "text/html"); -} - -void Client::send_error_response(unsigned code, const StringView& message, const HTTP::HttpRequest& request) -{ - StringBuilder builder; - builder.appendf("HTTP/1.0 %u ", code); - builder.append(message); - builder.append("\r\n\r\n"); - builder.append("<!DOCTYPE html><html><body><h1>"); - builder.appendf("%u ", code); - builder.append(message); - builder.append("</h1></body></html>"); - m_socket->write(builder.to_string()); - - log_response(code, request); -} - -void Client::log_response(unsigned code, const HTTP::HttpRequest& request) -{ - printf("%s :: %03u :: %s %s\n", - Core::DateTime::now().to_string().characters(), - code, - request.method_name().characters(), - request.resource().characters()); -} - -} diff --git a/Services/WebServer/Client.h b/Services/WebServer/Client.h deleted file mode 100644 index f363c9d9eb..0000000000 --- a/Services/WebServer/Client.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <LibCore/Object.h> -#include <LibCore/TCPSocket.h> -#include <LibHTTP/Forward.h> - -namespace WebServer { - -class Client final : public Core::Object { - C_OBJECT(Client); - -public: - void start(); - -private: - Client(NonnullRefPtr<Core::TCPSocket>, const String&, Core::Object* parent); - - void handle_request(ReadonlyBytes); - void send_response(StringView, const HTTP::HttpRequest&, const String& content_type); - void send_redirect(StringView redirect, const HTTP::HttpRequest& request); - void send_error_response(unsigned code, const StringView& message, const HTTP::HttpRequest&); - void die(); - void log_response(unsigned code, const HTTP::HttpRequest&); - void handle_directory_listing(const String& requested_path, const String& real_path, const HTTP::HttpRequest&); - - NonnullRefPtr<Core::TCPSocket> m_socket; - String m_root_path; -}; - -} diff --git a/Services/WebServer/main.cpp b/Services/WebServer/main.cpp deleted file mode 100644 index 8c326c84a8..0000000000 --- a/Services/WebServer/main.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Client.h" -#include <AK/MappedFile.h> -#include <LibCore/ArgsParser.h> -#include <LibCore/EventLoop.h> -#include <LibCore/File.h> -#include <LibCore/TCPServer.h> -#include <stdio.h> -#include <unistd.h> - -int main(int argc, char** argv) -{ - u16 default_port = 8000; - const char* root_path = "/www"; - - int port = default_port; - - Core::ArgsParser args_parser; - args_parser.add_option(port, "Port to listen on", "port", 'p', "port"); - args_parser.add_positional_argument(root_path, "Path to serve the contents of", "path", Core::ArgsParser::Required::No); - args_parser.parse(argc, argv); - - if ((u16)port != port) { - printf("Warning: invalid port number: %d\n", port); - port = default_port; - } - - auto real_root_path = Core::File::real_path_for(root_path); - - if (!Core::File::exists(real_root_path)) { - fprintf(stderr, "Root path does not exist: '%s'\n", root_path); - return 1; - } - - if (pledge("stdio accept rpath inet unix cpath fattr", nullptr) < 0) { - perror("pledge"); - return 1; - } - - Core::EventLoop loop; - - auto server = Core::TCPServer::construct(); - - server->on_ready_to_accept = [&] { - auto client_socket = server->accept(); - ASSERT(client_socket); - auto client = WebServer::Client::construct(client_socket.release_nonnull(), real_root_path, server); - client->start(); - }; - - if (!server->listen({}, port)) { - warnln("Failed to listen on 0.0.0.0:{}", port); - return 1; - } - - outln("Listening on 0.0.0.0:{}", port); - - if (unveil("/res/icons", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(real_root_path.characters(), "r") < 0) { - perror("unveil"); - return 1; - } - - unveil(nullptr, nullptr); - - if (pledge("stdio accept rpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - return loop.exec(); -} diff --git a/Services/WindowServer/AppletManager.cpp b/Services/WindowServer/AppletManager.cpp deleted file mode 100644 index 509d5de3d0..0000000000 --- a/Services/WindowServer/AppletManager.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "AppletManager.h" -#include <AK/QuickSort.h> -#include <LibGfx/Painter.h> -#include <WindowServer/MenuManager.h> - -namespace WindowServer { - -static AppletManager* s_the; -Vector<String> order_vector; - -AppletManager::AppletManager() -{ - s_the = this; - - auto wm_config = Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini"); - auto order = wm_config->read_entry("Applet", "Order"); - order_vector = order.split(','); -} - -AppletManager::~AppletManager() -{ -} - -AppletManager& AppletManager::the() -{ - ASSERT(s_the); - return *s_the; -} - -void AppletManager::event(Core::Event& event) -{ - auto& mouse_event = static_cast<MouseEvent&>(event); - - for (auto& applet : m_applets) { - if (!applet) - continue; - if (!applet->rect_in_menubar().contains(mouse_event.position())) - continue; - auto local_event = mouse_event.translated(-applet->rect_in_menubar().location()); - applet->event(local_event); - } -} - -void AppletManager::add_applet(Window& applet) -{ - m_applets.append(applet); - - // Prune any dead weak pointers from the applet list. - m_applets.remove_all_matching([](auto& entry) { - return entry.is_null(); - }); - - quick_sort(m_applets, [](auto& a, auto& b) { - auto index_a = order_vector.find_first_index(a->title()); - auto index_b = order_vector.find_first_index(b->title()); - return index_a.value_or(0) > index_b.value_or(0); - }); - - calculate_applet_rects(MenuManager::the().window()); - - MenuManager::the().refresh(); -} - -void AppletManager::calculate_applet_rects(Window& window) -{ - auto menubar_rect = window.rect(); - int right_edge_x = menubar_rect.width() - 4; - for (auto& existing_applet : m_applets) { - - Gfx::IntRect new_applet_rect(right_edge_x - existing_applet->size().width(), 0, existing_applet->size().width(), existing_applet->size().height()); - Gfx::IntRect dummy_menubar_rect(0, 0, 0, 18); - new_applet_rect.center_vertically_within(dummy_menubar_rect); - - existing_applet->set_rect_in_menubar(new_applet_rect); - right_edge_x = existing_applet->rect_in_menubar().x() - 4; - } -} - -void AppletManager::remove_applet(Window& applet) -{ - m_applets.remove_first_matching([&](auto& entry) { - return &applet == entry.ptr(); - }); - - MenuManager::the().refresh(); -} - -void AppletManager::draw() -{ - for (auto& applet : m_applets) { - if (!applet) - continue; - draw_applet(*applet); - } -} - -void AppletManager::draw_applet(const Window& applet) -{ - if (!applet.backing_store()) - return; - - Gfx::Painter painter(*MenuManager::the().window().backing_store()); - Gfx::PainterStateSaver saver(painter); - painter.add_clip_rect(applet.rect_in_menubar()); - painter.fill_rect(applet.rect_in_menubar(), WindowManager::the().palette().window()); - painter.blit(applet.rect_in_menubar().location(), *applet.backing_store(), applet.backing_store()->rect()); -} - -void AppletManager::invalidate_applet(const Window& applet, const Gfx::IntRect& rect) -{ - draw_applet(applet); - MenuManager::the().window().invalidate(rect.translated(applet.rect_in_menubar().location())); -} - -} diff --git a/Services/WindowServer/AppletManager.h b/Services/WindowServer/AppletManager.h deleted file mode 100644 index 92f39fbe6d..0000000000 --- a/Services/WindowServer/AppletManager.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <WindowServer/Window.h> -#include <WindowServer/WindowManager.h> - -namespace WindowServer { - -class AppletManager : public Core::Object { - C_OBJECT(AppletManager) -public: - AppletManager(); - ~AppletManager(); - - static AppletManager& the(); - - virtual void event(Core::Event&) override; - - void add_applet(Window& applet); - void remove_applet(Window& applet); - void draw(); - void invalidate_applet(const Window& applet, const Gfx::IntRect& rect); - void calculate_applet_rects(Window& window); - -private: - void draw_applet(const Window& applet); - - Vector<WeakPtr<Window>> m_applets; -}; - -} diff --git a/Services/WindowServer/Button.cpp b/Services/WindowServer/Button.cpp deleted file mode 100644 index 7265494b31..0000000000 --- a/Services/WindowServer/Button.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibGfx/CharacterBitmap.h> -#include <LibGfx/Painter.h> -#include <LibGfx/StylePainter.h> -#include <WindowServer/Button.h> -#include <WindowServer/Event.h> -#include <WindowServer/WindowManager.h> - -namespace WindowServer { - -Button::Button(WindowFrame& frame, Function<void(Button&)>&& on_click_handler) - : on_click(move(on_click_handler)) - , m_frame(frame) -{ -} - -Button::~Button() -{ -} - -void Button::paint(Gfx::Painter& painter) -{ - auto palette = WindowManager::the().palette(); - Gfx::PainterStateSaver saver(painter); - painter.translate(relative_rect().location()); - Gfx::StylePainter::paint_button(painter, rect(), palette, Gfx::ButtonStyle::Normal, m_pressed, m_hovered); - - if (m_icon) { - auto icon_location = rect().center().translated(-(m_icon->width() / 2), -(m_icon->height() / 2)); - if (m_pressed) - painter.translate(1, 1); - painter.blit(icon_location, *m_icon, m_icon->rect()); - } -} - -void Button::on_mouse_event(const MouseEvent& event) -{ - auto& wm = WindowManager::the(); - - if (event.type() == Event::MouseDown && event.button() == MouseButton::Left) { - m_pressed = true; - wm.set_cursor_tracking_button(this); - m_frame.invalidate(m_relative_rect); - return; - } - - if (event.type() == Event::MouseUp && event.button() == MouseButton::Left) { - if (wm.cursor_tracking_button() != this) - return; - wm.set_cursor_tracking_button(nullptr); - bool old_pressed = m_pressed; - m_pressed = false; - if (rect().contains(event.position())) { - if (on_click) - on_click(*this); - } - if (old_pressed != m_pressed) { - // Would like to compute: - // m_hovered = rect_after_action().contains(event.position()); - // However, we don't know that rect yet. We can make an educated - // guess which also looks okay even when wrong: - m_hovered = false; - m_frame.invalidate(m_relative_rect); - } - return; - } - - if (event.type() == Event::MouseMove) { - bool old_hovered = m_hovered; - m_hovered = rect().contains(event.position()); - wm.set_hovered_button(m_hovered ? this : nullptr); - if (old_hovered != m_hovered) - m_frame.invalidate(m_relative_rect); - } - - if (event.type() == Event::MouseMove && event.buttons() & (unsigned)MouseButton::Left) { - if (wm.cursor_tracking_button() != this) - return; - bool old_pressed = m_pressed; - m_pressed = m_hovered; - if (old_pressed != m_pressed) - m_frame.invalidate(m_relative_rect); - } -} - -Gfx::IntRect Button::screen_rect() const -{ - return m_relative_rect.translated(m_frame.rect().location()); -} - -} diff --git a/Services/WindowServer/Button.h b/Services/WindowServer/Button.h deleted file mode 100644 index 7a2d6b82e9..0000000000 --- a/Services/WindowServer/Button.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Function.h> -#include <AK/Weakable.h> -#include <LibGfx/Forward.h> -#include <LibGfx/Rect.h> - -namespace WindowServer { - -class MouseEvent; -class WindowFrame; - -class Button : public Weakable<Button> { -public: - Button(WindowFrame&, Function<void(Button&)>&& on_click_handler); - ~Button(); - - Gfx::IntRect relative_rect() const { return m_relative_rect; } - void set_relative_rect(const Gfx::IntRect& rect) { m_relative_rect = rect; } - - Gfx::IntRect rect() const { return { {}, m_relative_rect.size() }; } - Gfx::IntRect screen_rect() const; - - void paint(Gfx::Painter&); - - void on_mouse_event(const MouseEvent&); - - Function<void(Button&)> on_click; - - bool is_visible() const { return m_visible; } - - void set_icon(const Gfx::Bitmap& icon) { m_icon = icon; } - -private: - WindowFrame& m_frame; - Gfx::IntRect m_relative_rect; - RefPtr<Gfx::Bitmap> m_icon; - bool m_pressed { false }; - bool m_visible { true }; - bool m_hovered { false }; -}; - -} diff --git a/Services/WindowServer/CMakeLists.txt b/Services/WindowServer/CMakeLists.txt deleted file mode 100644 index b822fec6b5..0000000000 --- a/Services/WindowServer/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -compile_ipc(WindowServer.ipc WindowServerEndpoint.h) -compile_ipc(WindowClient.ipc WindowClientEndpoint.h) - -set(SOURCES - AppletManager.cpp - Button.cpp - ClientConnection.cpp - Compositor.cpp - Cursor.cpp - EventLoop.cpp - main.cpp - MenuBar.cpp - Menu.cpp - MenuItem.cpp - MenuManager.cpp - Screen.cpp - Window.cpp - WindowFrame.cpp - WindowManager.cpp - WindowSwitcher.cpp - WindowServerEndpoint.h - WindowClientEndpoint.h -) - -serenity_bin(WindowServer) -target_link_libraries(WindowServer LibCore LibGfx LibThread LibPthread LibIPC) diff --git a/Services/WindowServer/ClientConnection.cpp b/Services/WindowServer/ClientConnection.cpp deleted file mode 100644 index f0e4402d81..0000000000 --- a/Services/WindowServer/ClientConnection.cpp +++ /dev/null @@ -1,941 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Badge.h> -#include <AK/SharedBuffer.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/StandardCursor.h> -#include <LibGfx/SystemTheme.h> -#include <WindowServer/AppletManager.h> -#include <WindowServer/ClientConnection.h> -#include <WindowServer/Compositor.h> -#include <WindowServer/Menu.h> -#include <WindowServer/MenuBar.h> -#include <WindowServer/MenuItem.h> -#include <WindowServer/Screen.h> -#include <WindowServer/Window.h> -#include <WindowServer/WindowClientEndpoint.h> -#include <WindowServer/WindowManager.h> -#include <WindowServer/WindowSwitcher.h> -#include <errno.h> -#include <serenity.h> -#include <stdio.h> -#include <unistd.h> - -namespace WindowServer { - -HashMap<int, NonnullRefPtr<ClientConnection>>* s_connections; - -static Gfx::IntRect normalize_window_rect(Gfx::IntRect rect, WindowType window_type) -{ - auto min_size = 1; - if (window_type == WindowType::Normal) - min_size = 50; - Gfx::IntRect normalized_rect = { rect.x(), rect.y(), max(rect.width(), min_size), max(rect.height(), min_size) }; - return normalized_rect; -} - -void ClientConnection::for_each_client(Function<void(ClientConnection&)> callback) -{ - if (!s_connections) - return; - for (auto& it : *s_connections) { - callback(*it.value); - } -} - -ClientConnection* ClientConnection::from_client_id(int client_id) -{ - if (!s_connections) - return nullptr; - auto it = s_connections->find(client_id); - if (it == s_connections->end()) - return nullptr; - return (*it).value.ptr(); -} - -ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id) - : IPC::ClientConnection<WindowClientEndpoint, WindowServerEndpoint>(*this, move(client_socket), client_id) -{ - if (!s_connections) - s_connections = new HashMap<int, NonnullRefPtr<ClientConnection>>; - s_connections->set(client_id, *this); -} - -ClientConnection::~ClientConnection() -{ - if (m_has_display_link) - Compositor::the().decrement_display_link_count({}); - - MenuManager::the().close_all_menus_from_client({}, *this); - auto windows = move(m_windows); - for (auto& window : windows) { - window.value->detach_client({}); - if (window.value->type() == WindowType::MenuApplet) - AppletManager::the().remove_applet(window.value); - } -} - -void ClientConnection::die() -{ - deferred_invoke([this](auto&) { - s_connections->remove(client_id()); - }); -} - -void ClientConnection::notify_about_new_screen_rect(const Gfx::IntRect& rect) -{ - post_message(Messages::WindowClient::ScreenRectChanged(rect)); -} - -OwnPtr<Messages::WindowServer::CreateMenubarResponse> ClientConnection::handle(const Messages::WindowServer::CreateMenubar&) -{ - int menubar_id = m_next_menubar_id++; - auto menubar = make<MenuBar>(*this, menubar_id); - m_menubars.set(menubar_id, move(menubar)); - return make<Messages::WindowServer::CreateMenubarResponse>(menubar_id); -} - -OwnPtr<Messages::WindowServer::DestroyMenubarResponse> ClientConnection::handle(const Messages::WindowServer::DestroyMenubar& message) -{ - int menubar_id = message.menubar_id(); - auto it = m_menubars.find(menubar_id); - if (it == m_menubars.end()) { - did_misbehave("DestroyMenubar: Bad menubar ID"); - return {}; - } - auto& menubar = *(*it).value; - MenuManager::the().close_menubar(menubar); - m_menubars.remove(it); - return make<Messages::WindowServer::DestroyMenubarResponse>(); -} - -OwnPtr<Messages::WindowServer::CreateMenuResponse> ClientConnection::handle(const Messages::WindowServer::CreateMenu& message) -{ - int menu_id = m_next_menu_id++; - auto menu = Menu::construct(this, menu_id, message.menu_title()); - m_menus.set(menu_id, move(menu)); - return make<Messages::WindowServer::CreateMenuResponse>(menu_id); -} - -OwnPtr<Messages::WindowServer::DestroyMenuResponse> ClientConnection::handle(const Messages::WindowServer::DestroyMenu& message) -{ - int menu_id = message.menu_id(); - auto it = m_menus.find(menu_id); - if (it == m_menus.end()) { - did_misbehave("DestroyMenu: Bad menu ID"); - return {}; - } - auto& menu = *(*it).value; - menu.close(); - m_menus.remove(it); - remove_child(menu); - return make<Messages::WindowServer::DestroyMenuResponse>(); -} - -OwnPtr<Messages::WindowServer::SetApplicationMenubarResponse> ClientConnection::handle(const Messages::WindowServer::SetApplicationMenubar& message) -{ - int menubar_id = message.menubar_id(); - auto it = m_menubars.find(menubar_id); - if (it == m_menubars.end()) { - did_misbehave("SetApplicationMenubar: Bad menubar ID"); - return {}; - } - auto& menubar = *(*it).value; - m_app_menubar = menubar.make_weak_ptr(); - WindowManager::the().notify_client_changed_app_menubar(*this); - return make<Messages::WindowServer::SetApplicationMenubarResponse>(); -} - -OwnPtr<Messages::WindowServer::AddMenuToMenubarResponse> ClientConnection::handle(const Messages::WindowServer::AddMenuToMenubar& message) -{ - int menubar_id = message.menubar_id(); - int menu_id = message.menu_id(); - auto it = m_menubars.find(menubar_id); - auto jt = m_menus.find(menu_id); - if (it == m_menubars.end()) { - did_misbehave("AddMenuToMenubar: Bad menubar ID"); - return {}; - } - if (jt == m_menus.end()) { - did_misbehave("AddMenuToMenubar: Bad menu ID"); - return {}; - } - auto& menubar = *(*it).value; - auto& menu = *(*jt).value; - menubar.add_menu(menu); - return make<Messages::WindowServer::AddMenuToMenubarResponse>(); -} - -OwnPtr<Messages::WindowServer::AddMenuItemResponse> ClientConnection::handle(const Messages::WindowServer::AddMenuItem& message) -{ - int menu_id = message.menu_id(); - unsigned identifier = message.identifier(); - auto it = m_menus.find(menu_id); - if (it == m_menus.end()) { - dbg() << "AddMenuItem: Bad menu ID: " << menu_id; - return {}; - } - auto& menu = *(*it).value; - auto menu_item = make<MenuItem>(menu, identifier, message.text(), message.shortcut(), message.enabled(), message.checkable(), message.checked()); - if (message.is_default()) - menu_item->set_default(true); - if (message.icon_buffer_id() != -1) { - auto icon_buffer = SharedBuffer::create_from_shbuf_id(message.icon_buffer_id()); - if (!icon_buffer) - return {}; - // FIXME: Verify that the icon buffer can accommodate a 16x16 bitmap view. - auto shared_icon = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, icon_buffer.release_nonnull(), { 16, 16 }); - menu_item->set_icon(shared_icon); - } - menu_item->set_submenu_id(message.submenu_id()); - menu_item->set_exclusive(message.exclusive()); - menu.add_item(move(menu_item)); - return make<Messages::WindowServer::AddMenuItemResponse>(); -} - -OwnPtr<Messages::WindowServer::PopupMenuResponse> ClientConnection::handle(const Messages::WindowServer::PopupMenu& message) -{ - int menu_id = message.menu_id(); - auto position = message.screen_position(); - auto it = m_menus.find(menu_id); - if (it == m_menus.end()) { - did_misbehave("PopupMenu: Bad menu ID"); - return {}; - } - auto& menu = *(*it).value; - menu.popup(position); - return make<Messages::WindowServer::PopupMenuResponse>(); -} - -OwnPtr<Messages::WindowServer::DismissMenuResponse> ClientConnection::handle(const Messages::WindowServer::DismissMenu& message) -{ - int menu_id = message.menu_id(); - auto it = m_menus.find(menu_id); - if (it == m_menus.end()) { - did_misbehave("DismissMenu: Bad menu ID"); - return {}; - } - auto& menu = *(*it).value; - menu.close(); - return make<Messages::WindowServer::DismissMenuResponse>(); -} - -OwnPtr<Messages::WindowServer::UpdateMenuItemResponse> ClientConnection::handle(const Messages::WindowServer::UpdateMenuItem& message) -{ - int menu_id = message.menu_id(); - auto it = m_menus.find(menu_id); - if (it == m_menus.end()) { - did_misbehave("UpdateMenuItem: Bad menu ID"); - return {}; - } - auto& menu = *(*it).value; - auto* menu_item = menu.item_with_identifier(message.identifier()); - if (!menu_item) { - did_misbehave("UpdateMenuItem: Bad menu item identifier"); - return {}; - } - menu_item->set_text(message.text()); - menu_item->set_shortcut_text(message.shortcut()); - menu_item->set_enabled(message.enabled()); - menu_item->set_checkable(message.checkable()); - menu_item->set_default(message.is_default()); - if (message.checkable()) - menu_item->set_checked(message.checked()); - return make<Messages::WindowServer::UpdateMenuItemResponse>(); -} - -OwnPtr<Messages::WindowServer::AddMenuSeparatorResponse> ClientConnection::handle(const Messages::WindowServer::AddMenuSeparator& message) -{ - int menu_id = message.menu_id(); - auto it = m_menus.find(menu_id); - if (it == m_menus.end()) { - did_misbehave("AddMenuSeparator: Bad menu ID"); - return {}; - } - auto& menu = *(*it).value; - menu.add_item(make<MenuItem>(menu, MenuItem::Separator)); - return make<Messages::WindowServer::AddMenuSeparatorResponse>(); -} - -OwnPtr<Messages::WindowServer::MoveWindowToFrontResponse> ClientConnection::handle(const Messages::WindowServer::MoveWindowToFront& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("MoveWindowToFront: Bad window ID"); - return {}; - } - WindowManager::the().move_to_front_and_make_active(*(*it).value); - return make<Messages::WindowServer::MoveWindowToFrontResponse>(); -} - -OwnPtr<Messages::WindowServer::SetFullscreenResponse> ClientConnection::handle(const Messages::WindowServer::SetFullscreen& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetFullscreen: Bad window ID"); - return {}; - } - it->value->set_fullscreen(message.fullscreen()); - return make<Messages::WindowServer::SetFullscreenResponse>(); -} - -OwnPtr<Messages::WindowServer::SetWindowOpacityResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowOpacity& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowOpacity: Bad window ID"); - return {}; - } - it->value->set_opacity(message.opacity()); - return make<Messages::WindowServer::SetWindowOpacityResponse>(); -} - -void ClientConnection::handle(const Messages::WindowServer::AsyncSetWallpaper& message) -{ - Compositor::the().set_wallpaper(message.path(), [&](bool success) { - post_message(Messages::WindowClient::AsyncSetWallpaperFinished(success)); - }); -} - -OwnPtr<Messages::WindowServer::SetBackgroundColorResponse> ClientConnection::handle(const Messages::WindowServer::SetBackgroundColor& message) -{ - Compositor::the().set_background_color(message.background_color()); - return make<Messages::WindowServer::SetBackgroundColorResponse>(); -} - -OwnPtr<Messages::WindowServer::SetWallpaperModeResponse> ClientConnection::handle(const Messages::WindowServer::SetWallpaperMode& message) -{ - Compositor::the().set_wallpaper_mode(message.mode()); - return make<Messages::WindowServer::SetWallpaperModeResponse>(); -} - -OwnPtr<Messages::WindowServer::GetWallpaperResponse> ClientConnection::handle(const Messages::WindowServer::GetWallpaper&) -{ - return make<Messages::WindowServer::GetWallpaperResponse>(Compositor::the().wallpaper_path()); -} - -OwnPtr<Messages::WindowServer::SetResolutionResponse> ClientConnection::handle(const Messages::WindowServer::SetResolution& message) -{ - return make<Messages::WindowServer::SetResolutionResponse>(WindowManager::the().set_resolution(message.resolution().width(), message.resolution().height()), WindowManager::the().resolution()); -} - -OwnPtr<Messages::WindowServer::SetWindowTitleResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowTitle& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowTitle: Bad window ID"); - return {}; - } - it->value->set_title(message.title()); - return make<Messages::WindowServer::SetWindowTitleResponse>(); -} - -OwnPtr<Messages::WindowServer::GetWindowTitleResponse> ClientConnection::handle(const Messages::WindowServer::GetWindowTitle& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("GetWindowTitle: Bad window ID"); - return {}; - } - return make<Messages::WindowServer::GetWindowTitleResponse>(it->value->title()); -} - -OwnPtr<Messages::WindowServer::IsMaximizedResponse> ClientConnection::handle(const Messages::WindowServer::IsMaximized& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("IsMaximized: Bad window ID"); - return {}; - } - return make<Messages::WindowServer::IsMaximizedResponse>(it->value->is_maximized()); -} - -OwnPtr<Messages::WindowServer::SetWindowIconBitmapResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowIconBitmap& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowIconBitmap: Bad window ID"); - return {}; - } - auto& window = *(*it).value; - - if (message.icon().is_valid()) { - window.set_icon(*message.icon().bitmap()); - } else { - window.set_default_icon(); - } - - window.frame().invalidate_title_bar(); - WindowManager::the().tell_wm_listeners_window_icon_changed(window); - return make<Messages::WindowServer::SetWindowIconBitmapResponse>(); -} - -OwnPtr<Messages::WindowServer::SetWindowRectResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowRect& message) -{ - int window_id = message.window_id(); - auto it = m_windows.find(window_id); - if (it == m_windows.end()) { - did_misbehave("SetWindowRect: Bad window ID"); - return {}; - } - auto& window = *(*it).value; - if (window.is_fullscreen()) { - dbgln("ClientConnection: Ignoring SetWindowRect request for fullscreen window"); - return {}; - } - - if (message.rect().location() != window.rect().location()) { - window.set_default_positioned(false); - } - auto normalized_rect = normalize_window_rect(message.rect(), window.type()); - window.set_rect(normalized_rect); - window.request_update(normalized_rect); - return make<Messages::WindowServer::SetWindowRectResponse>(normalized_rect); -} - -OwnPtr<Messages::WindowServer::GetWindowRectResponse> ClientConnection::handle(const Messages::WindowServer::GetWindowRect& message) -{ - int window_id = message.window_id(); - auto it = m_windows.find(window_id); - if (it == m_windows.end()) { - did_misbehave("GetWindowRect: Bad window ID"); - return {}; - } - return make<Messages::WindowServer::GetWindowRectResponse>(it->value->rect()); -} - -OwnPtr<Messages::WindowServer::GetWindowRectInMenubarResponse> ClientConnection::handle(const Messages::WindowServer::GetWindowRectInMenubar& message) -{ - int window_id = message.window_id(); - auto it = m_windows.find(window_id); - if (it == m_windows.end()) { - did_misbehave("GetWindowRectInMenubar: Bad window ID"); - return {}; - } - return make<Messages::WindowServer::GetWindowRectInMenubarResponse>(it->value->rect_in_menubar()); -} - -Window* ClientConnection::window_from_id(i32 window_id) -{ - auto it = m_windows.find(window_id); - if (it == m_windows.end()) - return nullptr; - return it->value.ptr(); -} - -OwnPtr<Messages::WindowServer::CreateWindowResponse> ClientConnection::handle(const Messages::WindowServer::CreateWindow& message) -{ - Window* parent_window = nullptr; - if (message.parent_window_id()) { - parent_window = window_from_id(message.parent_window_id()); - if (!parent_window) { - did_misbehave("CreateWindow with bad parent_window_id"); - return {}; - } - } - - int window_id = m_next_window_id++; - auto window = Window::construct(*this, (WindowType)message.type(), window_id, message.modal(), message.minimizable(), message.frameless(), message.resizable(), message.fullscreen(), message.accessory(), parent_window); - - window->set_has_alpha_channel(message.has_alpha_channel()); - window->set_title(message.title()); - if (!message.fullscreen()) { - auto rect = message.rect(); - if (message.auto_position() && window->type() == WindowType::Normal) { - rect = { WindowManager::the().get_recommended_window_position({ 100, 100 }), message.rect().size() }; - window->set_default_positioned(true); - } - auto normalized_rect = normalize_window_rect(rect, window->type()); - window->set_rect(normalized_rect); - } - if (window->type() == WindowType::Desktop) { - window->set_rect(WindowManager::the().desktop_rect()); - window->recalculate_rect(); - } - window->set_opacity(message.opacity()); - window->set_size_increment(message.size_increment()); - window->set_base_size(message.base_size()); - window->set_resize_aspect_ratio(message.resize_aspect_ratio()); - window->invalidate(); - if (window->type() == WindowType::MenuApplet) - AppletManager::the().add_applet(*window); - m_windows.set(window_id, move(window)); - return make<Messages::WindowServer::CreateWindowResponse>(window_id); -} - -void ClientConnection::destroy_window(Window& window, Vector<i32>& destroyed_window_ids) -{ - for (auto& child_window : window.child_windows()) { - if (!child_window) - continue; - ASSERT(child_window->window_id() != window.window_id()); - destroy_window(*child_window, destroyed_window_ids); - } - - for (auto& accessory_window : window.accessory_windows()) { - if (!accessory_window) - continue; - ASSERT(accessory_window->window_id() != window.window_id()); - destroy_window(*accessory_window, destroyed_window_ids); - } - - destroyed_window_ids.append(window.window_id()); - - if (window.type() == WindowType::MenuApplet) - AppletManager::the().remove_applet(window); - - window.destroy(); - remove_child(window); - m_windows.remove(window.window_id()); -} - -OwnPtr<Messages::WindowServer::DestroyWindowResponse> ClientConnection::handle(const Messages::WindowServer::DestroyWindow& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("DestroyWindow: Bad window ID"); - return {}; - } - auto& window = *(*it).value; - Vector<i32> destroyed_window_ids; - destroy_window(window, destroyed_window_ids); - return make<Messages::WindowServer::DestroyWindowResponse>(destroyed_window_ids); -} - -void ClientConnection::post_paint_message(Window& window, bool ignore_occlusion) -{ - auto rect_set = window.take_pending_paint_rects(); - if (window.is_minimized() || (!ignore_occlusion && window.is_occluded())) - return; - - post_message(Messages::WindowClient::Paint(window.window_id(), window.size(), rect_set.rects())); -} - -void ClientConnection::handle(const Messages::WindowServer::InvalidateRect& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("InvalidateRect: Bad window ID"); - return; - } - auto& window = *(*it).value; - for (size_t i = 0; i < message.rects().size(); ++i) - window.request_update(message.rects()[i].intersected({ {}, window.size() }), message.ignore_occlusion()); -} - -void ClientConnection::handle(const Messages::WindowServer::DidFinishPainting& message) -{ - int window_id = message.window_id(); - auto it = m_windows.find(window_id); - if (it == m_windows.end()) { - did_misbehave("DidFinishPainting: Bad window ID"); - return; - } - auto& window = *(*it).value; - for (auto& rect : message.rects()) - window.invalidate(rect); - - WindowSwitcher::the().refresh_if_needed(); -} - -OwnPtr<Messages::WindowServer::SetWindowBackingStoreResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowBackingStore& message) -{ - int window_id = message.window_id(); - auto it = m_windows.find(window_id); - if (it == m_windows.end()) { - did_misbehave("SetWindowBackingStore: Bad window ID"); - return {}; - } - auto& window = *(*it).value; - if (window.last_backing_store() && window.last_backing_store()->shbuf_id() == message.shbuf_id()) { - window.swap_backing_stores(); - } else { - auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.shbuf_id()); - if (!shared_buffer) - return make<Messages::WindowServer::SetWindowBackingStoreResponse>(); - auto backing_store = Gfx::Bitmap::create_with_shared_buffer( - message.has_alpha_channel() ? Gfx::BitmapFormat::RGBA32 : Gfx::BitmapFormat::RGB32, - *shared_buffer, - message.size()); - window.set_backing_store(move(backing_store)); - } - - if (message.flush_immediately()) - window.invalidate(false); - - return make<Messages::WindowServer::SetWindowBackingStoreResponse>(); -} - -OwnPtr<Messages::WindowServer::SetGlobalCursorTrackingResponse> ClientConnection::handle(const Messages::WindowServer::SetGlobalCursorTracking& message) -{ - int window_id = message.window_id(); - auto it = m_windows.find(window_id); - if (it == m_windows.end()) { - did_misbehave("SetGlobalCursorTracking: Bad window ID"); - return {}; - } - it->value->set_global_cursor_tracking_enabled(message.enabled()); - return make<Messages::WindowServer::SetGlobalCursorTrackingResponse>(); -} - -OwnPtr<Messages::WindowServer::SetWindowCursorResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowCursor& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowCursor: Bad window ID"); - return {}; - } - auto& window = *(*it).value; - if (message.cursor_type() < 0 || message.cursor_type() >= (i32)Gfx::StandardCursor::__Count) { - did_misbehave("SetWindowCursor: Bad cursor type"); - return {}; - } - window.set_cursor(Cursor::create((Gfx::StandardCursor)message.cursor_type())); - Compositor::the().invalidate_cursor(); - return make<Messages::WindowServer::SetWindowCursorResponse>(); -} - -OwnPtr<Messages::WindowServer::SetWindowCustomCursorResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowCustomCursor& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowCustomCursor: Bad window ID"); - return {}; - } - - auto& window = *(*it).value; - if (!message.cursor().is_valid()) { - did_misbehave("SetWindowCustomCursor: Bad cursor"); - return {}; - } - - window.set_cursor(Cursor::create(*message.cursor().bitmap())); - Compositor::the().invalidate_cursor(); - return make<Messages::WindowServer::SetWindowCustomCursorResponse>(); -} - -OwnPtr<Messages::WindowServer::SetWindowHasAlphaChannelResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowHasAlphaChannel& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowHasAlphaChannel: Bad window ID"); - return {}; - } - it->value->set_has_alpha_channel(message.has_alpha_channel()); - return make<Messages::WindowServer::SetWindowHasAlphaChannelResponse>(); -} - -void ClientConnection::handle(const Messages::WindowServer::WM_SetActiveWindow& message) -{ - auto* client = ClientConnection::from_client_id(message.client_id()); - if (!client) { - did_misbehave("WM_SetActiveWindow: Bad client ID"); - return; - } - auto it = client->m_windows.find(message.window_id()); - if (it == client->m_windows.end()) { - did_misbehave("WM_SetActiveWindow: Bad window ID"); - return; - } - auto& window = *(*it).value; - WindowManager::the().minimize_windows(window, false); - WindowManager::the().move_to_front_and_make_active(window); -} - -void ClientConnection::handle(const Messages::WindowServer::WM_PopupWindowMenu& message) -{ - auto* client = ClientConnection::from_client_id(message.client_id()); - if (!client) { - did_misbehave("WM_PopupWindowMenu: Bad client ID"); - return; - } - auto it = client->m_windows.find(message.window_id()); - if (it == client->m_windows.end()) { - did_misbehave("WM_PopupWindowMenu: Bad window ID"); - return; - } - auto& window = *(*it).value; - if (auto* modal_window = window.blocking_modal_window()) { - modal_window->popup_window_menu(message.screen_position(), WindowMenuDefaultAction::BasedOnWindowState); - } else { - window.popup_window_menu(message.screen_position(), WindowMenuDefaultAction::BasedOnWindowState); - } -} - -void ClientConnection::handle(const Messages::WindowServer::WM_StartWindowResize& request) -{ - auto* client = ClientConnection::from_client_id(request.client_id()); - if (!client) { - did_misbehave("WM_StartWindowResize: Bad client ID"); - return; - } - auto it = client->m_windows.find(request.window_id()); - if (it == client->m_windows.end()) { - did_misbehave("WM_StartWindowResize: Bad window ID"); - return; - } - auto& window = *(*it).value; - // FIXME: We are cheating a bit here by using the current cursor location and hard-coding the left button. - // Maybe the client should be allowed to specify what initiated this request? - WindowManager::the().start_window_resize(window, Screen::the().cursor_location(), MouseButton::Left); -} - -void ClientConnection::handle(const Messages::WindowServer::WM_SetWindowMinimized& message) -{ - auto* client = ClientConnection::from_client_id(message.client_id()); - if (!client) { - did_misbehave("WM_SetWindowMinimized: Bad client ID"); - return; - } - auto it = client->m_windows.find(message.window_id()); - if (it == client->m_windows.end()) { - did_misbehave("WM_SetWindowMinimized: Bad window ID"); - return; - } - auto& window = *(*it).value; - WindowManager::the().minimize_windows(window, message.minimized()); -} - -OwnPtr<Messages::WindowServer::GreetResponse> ClientConnection::handle(const Messages::WindowServer::Greet&) -{ - return make<Messages::WindowServer::GreetResponse>(client_id(), Screen::the().rect(), Gfx::current_system_theme_buffer_id()); -} - -void ClientConnection::handle(const Messages::WindowServer::WM_SetWindowTaskbarRect& message) -{ - // Because the Taskbar (which should be the only user of this API) does not own the - // window or the client id, there is a possibility that it may send this message for - // a window or client that may have been destroyed already. This is not an error, - // and we should not call did_misbehave() for either. - auto* client = ClientConnection::from_client_id(message.client_id()); - if (!client) - return; - - auto it = client->m_windows.find(message.window_id()); - if (it == client->m_windows.end()) - return; - - auto& window = *(*it).value; - window.set_taskbar_rect(message.rect()); -} - -OwnPtr<Messages::WindowServer::StartDragResponse> ClientConnection::handle(const Messages::WindowServer::StartDrag& message) -{ - auto& wm = WindowManager::the(); - if (wm.dnd_client()) - return make<Messages::WindowServer::StartDragResponse>(false); - - RefPtr<Gfx::Bitmap> bitmap; - if (message.bitmap_id() != -1) { - auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.bitmap_id()); - ssize_t size_in_bytes = message.bitmap_size().area() * sizeof(Gfx::RGBA32); - if (size_in_bytes > shared_buffer->size()) { - did_misbehave("SetAppletBackingStore: Shared buffer is too small for applet size"); - return {}; - } - bitmap = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *shared_buffer, message.bitmap_size()); - } - - wm.start_dnd_drag(*this, message.text(), bitmap, Core::MimeData::construct(message.mime_data())); - return make<Messages::WindowServer::StartDragResponse>(true); -} - -OwnPtr<Messages::WindowServer::SetSystemMenuResponse> ClientConnection::handle(const Messages::WindowServer::SetSystemMenu& message) -{ - auto it = m_menus.find(message.menu_id()); - if (it == m_menus.end()) { - did_misbehave("SetSystemMenu called with invalid menu ID"); - return {}; - } - - auto& menu = it->value; - MenuManager::the().set_system_menu(menu); - return make<Messages::WindowServer::SetSystemMenuResponse>(); -} - -OwnPtr<Messages::WindowServer::SetSystemThemeResponse> ClientConnection::handle(const Messages::WindowServer::SetSystemTheme& message) -{ - bool success = WindowManager::the().update_theme(message.theme_path(), message.theme_name()); - return make<Messages::WindowServer::SetSystemThemeResponse>(success); -} - -OwnPtr<Messages::WindowServer::GetSystemThemeResponse> ClientConnection::handle(const Messages::WindowServer::GetSystemTheme&) -{ - auto wm_config = Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini"); - auto name = wm_config->read_entry("Theme", "Name"); - return make<Messages::WindowServer::GetSystemThemeResponse>(name); -} - -void ClientConnection::boost() -{ - // FIXME: Re-enable this when we have a solution for boosting. -#if 0 - if (set_process_boost(client_pid(), 10) < 0) - perror("boost: set_process_boost"); -#endif -} - -void ClientConnection::deboost() -{ - // FIXME: Re-enable this when we have a solution for boosting. -#if 0 - if (set_process_boost(client_pid(), 0) < 0) - perror("deboost: set_process_boost"); -#endif -} - -OwnPtr<Messages::WindowServer::SetWindowBaseSizeAndSizeIncrementResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowBaseSizeAndSizeIncrement& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowBaseSizeAndSizeIncrementResponse: Bad window ID"); - return {}; - } - - auto& window = *it->value; - window.set_base_size(message.base_size()); - window.set_size_increment(message.size_increment()); - - return make<Messages::WindowServer::SetWindowBaseSizeAndSizeIncrementResponse>(); -} - -OwnPtr<Messages::WindowServer::SetWindowResizeAspectRatioResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowResizeAspectRatio& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowResizeAspectRatioResponse: Bad window ID"); - return {}; - } - - auto& window = *it->value; - window.set_resize_aspect_ratio(message.resize_aspect_ratio()); - - return make<Messages::WindowServer::SetWindowResizeAspectRatioResponse>(); -} - -void ClientConnection::handle(const Messages::WindowServer::EnableDisplayLink&) -{ - if (m_has_display_link) - return; - m_has_display_link = true; - Compositor::the().increment_display_link_count({}); -} - -void ClientConnection::handle(const Messages::WindowServer::DisableDisplayLink&) -{ - if (!m_has_display_link) - return; - m_has_display_link = false; - Compositor::the().decrement_display_link_count({}); -} - -void ClientConnection::notify_display_link(Badge<Compositor>) -{ - if (!m_has_display_link) - return; - - post_message(Messages::WindowClient::DisplayLinkNotification()); -} - -void ClientConnection::handle(const Messages::WindowServer::SetWindowProgress& message) -{ - auto it = m_windows.find(message.window_id()); - if (it == m_windows.end()) { - did_misbehave("SetWindowProgress with bad window ID"); - return; - } - it->value->set_progress(message.progress()); -} - -void ClientConnection::handle(const Messages::WindowServer::Pong&) -{ - m_ping_timer = nullptr; - set_unresponsive(false); -} - -OwnPtr<Messages::WindowServer::GetGlobalCursorPositionResponse> ClientConnection::handle(const Messages::WindowServer::GetGlobalCursorPosition&) -{ - return make<Messages::WindowServer::GetGlobalCursorPositionResponse>(Screen::the().cursor_location()); -} - -OwnPtr<Messages::WindowServer::SetMouseAccelerationResponse> ClientConnection::handle(const Messages::WindowServer::SetMouseAcceleration& message) -{ - if (message.factor() < mouse_accel_min || message.factor() > mouse_accel_max) { - did_misbehave("SetMouseAcceleration with bad acceleration factor"); - return {}; - } - WindowManager::the().set_acceleration_factor(message.factor()); - return make<Messages::WindowServer::SetMouseAccelerationResponse>(); -} - -OwnPtr<Messages::WindowServer::GetMouseAccelerationResponse> ClientConnection::handle(const Messages::WindowServer::GetMouseAcceleration&) -{ - return make<Messages::WindowServer::GetMouseAccelerationResponse>(Screen::the().acceleration_factor()); -} - -OwnPtr<Messages::WindowServer::SetScrollStepSizeResponse> ClientConnection::handle(const Messages::WindowServer::SetScrollStepSize& message) -{ - if (message.step_size() < scroll_step_size_min) { - did_misbehave("SetScrollStepSize with bad scroll step size"); - return {}; - } - WindowManager::the().set_scroll_step_size(message.step_size()); - return make<Messages::WindowServer::SetScrollStepSizeResponse>(); -} -OwnPtr<Messages::WindowServer::GetScrollStepSizeResponse> ClientConnection::handle(const Messages::WindowServer::GetScrollStepSize&) -{ - return make<Messages::WindowServer::GetScrollStepSizeResponse>(Screen::the().scroll_step_size()); -} - -void ClientConnection::set_unresponsive(bool unresponsive) -{ - if (m_unresponsive == unresponsive) - return; - m_unresponsive = unresponsive; - for (auto& it : m_windows) { - auto& window = *it.value; - window.invalidate(); - if (unresponsive) - window.set_cursor(WindowManager::the().wait_cursor()); - } - Compositor::the().invalidate_cursor(); -} - -void ClientConnection::may_have_become_unresponsive() -{ - post_message(Messages::WindowClient::Ping()); - m_ping_timer = Core::Timer::create_single_shot(1000, [this] { - set_unresponsive(true); - }); -} - -void ClientConnection::did_become_responsive() -{ - set_unresponsive(false); -} - -} diff --git a/Services/WindowServer/ClientConnection.h b/Services/WindowServer/ClientConnection.h deleted file mode 100644 index 12174e9189..0000000000 --- a/Services/WindowServer/ClientConnection.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Badge.h> -#include <AK/Function.h> -#include <AK/HashMap.h> -#include <AK/OwnPtr.h> -#include <AK/WeakPtr.h> -#include <LibCore/Object.h> -#include <LibGfx/Bitmap.h> -#include <LibIPC/ClientConnection.h> -#include <WindowServer/Event.h> -#include <WindowServer/WindowClientEndpoint.h> -#include <WindowServer/WindowServerEndpoint.h> - -namespace WindowServer { - -class Compositor; -class Window; -class Menu; -class MenuBar; - -class ClientConnection final - : public IPC::ClientConnection<WindowClientEndpoint, WindowServerEndpoint> - , public WindowServerEndpoint { - C_OBJECT(ClientConnection) -public: - ~ClientConnection() override; - - bool is_unresponsive() const { return m_unresponsive; } - - void boost(); - void deboost(); - - static ClientConnection* from_client_id(int client_id); - static void for_each_client(Function<void(ClientConnection&)>); - - MenuBar* app_menubar() { return m_app_menubar.ptr(); } - - void notify_about_new_screen_rect(const Gfx::IntRect&); - void post_paint_message(Window&, bool ignore_occlusion = false); - - Menu* find_menu_by_id(int menu_id) - { - auto menu = m_menus.get(menu_id); - if (!menu.has_value()) - return nullptr; - return const_cast<Menu*>(menu.value().ptr()); - } - const Menu* find_menu_by_id(int menu_id) const - { - auto menu = m_menus.get(menu_id); - if (!menu.has_value()) - return nullptr; - return menu.value().ptr(); - } - - void notify_display_link(Badge<Compositor>); - -private: - explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); - - // ^ClientConnection - virtual void die() override; - virtual void may_have_become_unresponsive() override; - virtual void did_become_responsive() override; - - void set_unresponsive(bool); - void destroy_window(Window&, Vector<i32>& destroyed_window_ids); - - virtual OwnPtr<Messages::WindowServer::GreetResponse> handle(const Messages::WindowServer::Greet&) override; - virtual OwnPtr<Messages::WindowServer::CreateMenubarResponse> handle(const Messages::WindowServer::CreateMenubar&) override; - virtual OwnPtr<Messages::WindowServer::DestroyMenubarResponse> handle(const Messages::WindowServer::DestroyMenubar&) override; - virtual OwnPtr<Messages::WindowServer::CreateMenuResponse> handle(const Messages::WindowServer::CreateMenu&) override; - virtual OwnPtr<Messages::WindowServer::DestroyMenuResponse> handle(const Messages::WindowServer::DestroyMenu&) override; - virtual OwnPtr<Messages::WindowServer::AddMenuToMenubarResponse> handle(const Messages::WindowServer::AddMenuToMenubar&) override; - virtual OwnPtr<Messages::WindowServer::SetApplicationMenubarResponse> handle(const Messages::WindowServer::SetApplicationMenubar&) override; - virtual OwnPtr<Messages::WindowServer::AddMenuItemResponse> handle(const Messages::WindowServer::AddMenuItem&) override; - virtual OwnPtr<Messages::WindowServer::AddMenuSeparatorResponse> handle(const Messages::WindowServer::AddMenuSeparator&) override; - virtual OwnPtr<Messages::WindowServer::UpdateMenuItemResponse> handle(const Messages::WindowServer::UpdateMenuItem&) override; - virtual OwnPtr<Messages::WindowServer::CreateWindowResponse> handle(const Messages::WindowServer::CreateWindow&) override; - virtual OwnPtr<Messages::WindowServer::DestroyWindowResponse> handle(const Messages::WindowServer::DestroyWindow&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowTitleResponse> handle(const Messages::WindowServer::SetWindowTitle&) override; - virtual OwnPtr<Messages::WindowServer::GetWindowTitleResponse> handle(const Messages::WindowServer::GetWindowTitle&) override; - virtual OwnPtr<Messages::WindowServer::IsMaximizedResponse> handle(const Messages::WindowServer::IsMaximized&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowRectResponse> handle(const Messages::WindowServer::SetWindowRect&) override; - virtual OwnPtr<Messages::WindowServer::GetWindowRectResponse> handle(const Messages::WindowServer::GetWindowRect&) override; - virtual OwnPtr<Messages::WindowServer::GetWindowRectInMenubarResponse> handle(const Messages::WindowServer::GetWindowRectInMenubar&) override; - virtual void handle(const Messages::WindowServer::InvalidateRect&) override; - virtual void handle(const Messages::WindowServer::DidFinishPainting&) override; - virtual OwnPtr<Messages::WindowServer::SetGlobalCursorTrackingResponse> handle(const Messages::WindowServer::SetGlobalCursorTracking&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowOpacityResponse> handle(const Messages::WindowServer::SetWindowOpacity&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowBackingStoreResponse> handle(const Messages::WindowServer::SetWindowBackingStore&) override; - virtual void handle(const Messages::WindowServer::WM_SetActiveWindow&) override; - virtual void handle(const Messages::WindowServer::WM_SetWindowMinimized&) override; - virtual void handle(const Messages::WindowServer::WM_StartWindowResize&) override; - virtual void handle(const Messages::WindowServer::WM_PopupWindowMenu&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowHasAlphaChannelResponse> handle(const Messages::WindowServer::SetWindowHasAlphaChannel&) override; - virtual OwnPtr<Messages::WindowServer::MoveWindowToFrontResponse> handle(const Messages::WindowServer::MoveWindowToFront&) override; - virtual OwnPtr<Messages::WindowServer::SetFullscreenResponse> handle(const Messages::WindowServer::SetFullscreen&) override; - virtual void handle(const Messages::WindowServer::AsyncSetWallpaper&) override; - virtual OwnPtr<Messages::WindowServer::SetBackgroundColorResponse> handle(const Messages::WindowServer::SetBackgroundColor&) override; - virtual OwnPtr<Messages::WindowServer::SetWallpaperModeResponse> handle(const Messages::WindowServer::SetWallpaperMode&) override; - virtual OwnPtr<Messages::WindowServer::GetWallpaperResponse> handle(const Messages::WindowServer::GetWallpaper&) override; - virtual OwnPtr<Messages::WindowServer::SetResolutionResponse> handle(const Messages::WindowServer::SetResolution&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowCursorResponse> handle(const Messages::WindowServer::SetWindowCursor&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowCustomCursorResponse> handle(const Messages::WindowServer::SetWindowCustomCursor&) override; - virtual OwnPtr<Messages::WindowServer::PopupMenuResponse> handle(const Messages::WindowServer::PopupMenu&) override; - virtual OwnPtr<Messages::WindowServer::DismissMenuResponse> handle(const Messages::WindowServer::DismissMenu&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowIconBitmapResponse> handle(const Messages::WindowServer::SetWindowIconBitmap&) override; - virtual void handle(const Messages::WindowServer::WM_SetWindowTaskbarRect&) override; - virtual OwnPtr<Messages::WindowServer::StartDragResponse> handle(const Messages::WindowServer::StartDrag&) override; - virtual OwnPtr<Messages::WindowServer::SetSystemMenuResponse> handle(const Messages::WindowServer::SetSystemMenu&) override; - virtual OwnPtr<Messages::WindowServer::SetSystemThemeResponse> handle(const Messages::WindowServer::SetSystemTheme&) override; - virtual OwnPtr<Messages::WindowServer::GetSystemThemeResponse> handle(const Messages::WindowServer::GetSystemTheme&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowBaseSizeAndSizeIncrementResponse> handle(const Messages::WindowServer::SetWindowBaseSizeAndSizeIncrement&) override; - virtual OwnPtr<Messages::WindowServer::SetWindowResizeAspectRatioResponse> handle(const Messages::WindowServer::SetWindowResizeAspectRatio&) override; - virtual void handle(const Messages::WindowServer::EnableDisplayLink&) override; - virtual void handle(const Messages::WindowServer::DisableDisplayLink&) override; - virtual void handle(const Messages::WindowServer::SetWindowProgress&) override; - virtual void handle(const Messages::WindowServer::Pong&) override; - virtual OwnPtr<Messages::WindowServer::GetGlobalCursorPositionResponse> handle(const Messages::WindowServer::GetGlobalCursorPosition&) override; - virtual OwnPtr<Messages::WindowServer::SetMouseAccelerationResponse> handle(const Messages::WindowServer::SetMouseAcceleration&) override; - virtual OwnPtr<Messages::WindowServer::GetMouseAccelerationResponse> handle(const Messages::WindowServer::GetMouseAcceleration&) override; - virtual OwnPtr<Messages::WindowServer::SetScrollStepSizeResponse> handle(const Messages::WindowServer::SetScrollStepSize&) override; - virtual OwnPtr<Messages::WindowServer::GetScrollStepSizeResponse> handle(const Messages::WindowServer::GetScrollStepSize&) override; - - Window* window_from_id(i32 window_id); - - HashMap<int, NonnullRefPtr<Window>> m_windows; - HashMap<int, NonnullOwnPtr<MenuBar>> m_menubars; - HashMap<int, NonnullRefPtr<Menu>> m_menus; - WeakPtr<MenuBar> m_app_menubar; - - RefPtr<Core::Timer> m_ping_timer; - - int m_next_menubar_id { 10000 }; - int m_next_menu_id { 20000 }; - int m_next_window_id { 1982 }; - - bool m_has_display_link { false }; - bool m_unresponsive { false }; -}; - -} diff --git a/Services/WindowServer/Compositor.cpp b/Services/WindowServer/Compositor.cpp deleted file mode 100644 index 470a8e53a7..0000000000 --- a/Services/WindowServer/Compositor.cpp +++ /dev/null @@ -1,1040 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Compositor.h" -#include "ClientConnection.h" -#include "Event.h" -#include "EventLoop.h" -#include "Screen.h" -#include "Window.h" -#include "WindowManager.h" -#include <AK/Memory.h> -#include <AK/ScopeGuard.h> -#include <LibCore/Timer.h> -#include <LibGfx/Font.h> -#include <LibGfx/Painter.h> -#include <LibGfx/StylePainter.h> -#include <LibThread/BackgroundAction.h> - -//#define COMPOSE_DEBUG -//#define OCCLUSIONS_DEBUG - -namespace WindowServer { - -Compositor& Compositor::the() -{ - static Compositor s_the; - return s_the; -} - -static WallpaperMode mode_to_enum(const String& name) -{ - if (name == "simple") - return WallpaperMode::Simple; - if (name == "tile") - return WallpaperMode::Tile; - if (name == "center") - return WallpaperMode::Center; - if (name == "scaled") - return WallpaperMode::Scaled; - return WallpaperMode::Simple; -} - -Compositor::Compositor() -{ - m_display_link_notify_timer = add<Core::Timer>( - 1000 / 60, [this] { - notify_display_links(); - }); - m_display_link_notify_timer->stop(); - - m_compose_timer = Core::Timer::create_single_shot( - 1000 / 60, - [this] { - compose(); - }, - this); - - m_immediate_compose_timer = Core::Timer::create_single_shot( - 0, - [this] { - compose(); - }, - this); - - m_screen_can_set_buffer = Screen::the().can_set_buffer(); - init_bitmaps(); -} - -void Compositor::init_bitmaps() -{ - auto& screen = Screen::the(); - auto size = screen.size(); - - m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, size, screen.pitch(), screen.scanline(0)); - - if (m_screen_can_set_buffer) - m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::RGB32, size, screen.pitch(), screen.scanline(size.height())); - else - m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size); - - m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, size); - - m_front_painter = make<Gfx::Painter>(*m_front_bitmap); - m_back_painter = make<Gfx::Painter>(*m_back_bitmap); - m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap); - - m_buffers_are_flipped = false; - - invalidate_screen(); -} - -void Compositor::did_construct_window_manager(Badge<WindowManager>) -{ - auto& wm = WindowManager::the(); - m_wallpaper_mode = mode_to_enum(wm.config()->read_entry("Background", "Mode", "simple")); - m_custom_background_color = Color::from_string(wm.config()->read_entry("Background", "Color", "")); - - invalidate_screen(); - invalidate_occlusions(); - compose(); -} - -void Compositor::compose() -{ - auto& wm = WindowManager::the(); - auto& ws = Screen::the(); - - { - auto& current_cursor = wm.active_cursor(); - if (m_current_cursor != ¤t_cursor) - change_cursor(¤t_cursor); - } - - if (!m_invalidated_any) { - // nothing dirtied since the last compose pass. - return; - } - - if (m_occlusions_dirty) { - m_occlusions_dirty = false; - recompute_occlusions(); - } - - auto dirty_screen_rects = move(m_dirty_screen_rects); - dirty_screen_rects.add(m_last_geometry_label_damage_rect.intersected(ws.rect())); - dirty_screen_rects.add(m_last_dnd_rect.intersected(ws.rect())); - if (m_invalidated_cursor) { - if (wm.dnd_client()) - dirty_screen_rects.add(wm.dnd_rect().intersected(ws.rect())); - } - - // Mark window regions as dirty that need to be re-rendered - wm.for_each_visible_window_from_back_to_front([&](Window& window) { - auto frame_rect = window.frame().rect(); - for (auto& dirty_rect : dirty_screen_rects.rects()) { - auto invalidate_rect = dirty_rect.intersected(frame_rect); - if (!invalidate_rect.is_empty()) { - auto inner_rect_offset = window.rect().location() - frame_rect.location(); - invalidate_rect.move_by(-(frame_rect.location() + inner_rect_offset)); - window.invalidate_no_notify(invalidate_rect); - m_invalidated_window = true; - } - } - window.prepare_dirty_rects(); - return IterationDecision::Continue; - }); - - // Any windows above or below a given window that need to be re-rendered - // also require us to re-render that window's intersecting area, regardless - // of whether that window has any dirty rectangles - wm.for_each_visible_window_from_back_to_front([&](Window& window) { - auto& transparency_rects = window.transparency_rects(); - if (transparency_rects.is_empty()) - return IterationDecision::Continue; - - auto frame_rect = window.frame().rect(); - auto& dirty_rects = window.dirty_rects(); - wm.for_each_visible_window_from_back_to_front([&](Window& w) { - if (&w == &window) - return IterationDecision::Continue; - auto frame_rect2 = w.frame().rect(); - if (!frame_rect2.intersects(frame_rect)) - return IterationDecision::Continue; - transparency_rects.for_each_intersected(w.dirty_rects(), [&](const Gfx::IntRect& intersected_dirty) { - dirty_rects.add(intersected_dirty); - return IterationDecision::Continue; - }); - return IterationDecision::Continue; - }); - return IterationDecision::Continue; - }); - - Color background_color = wm.palette().desktop_background(); - if (m_custom_background_color.has_value()) - background_color = m_custom_background_color.value(); - -#ifdef COMPOSE_DEBUG - dbg() << "COMPOSE: invalidated: window:" << m_invalidated_window << " cursor:" << m_invalidated_cursor << " any: " << m_invalidated_any; - for (auto& r : dirty_screen_rects.rects()) - dbg() << "dirty screen: " << r; -#endif - Gfx::DisjointRectSet flush_rects; - Gfx::DisjointRectSet flush_transparent_rects; - Gfx::DisjointRectSet flush_special_rects; - auto cursor_rect = current_cursor_rect(); - bool need_to_draw_cursor = false; - - auto check_restore_cursor_back = [&](const Gfx::IntRect& rect) { - if (!need_to_draw_cursor && rect.intersects(cursor_rect)) { - // Restore what's behind the cursor if anything touches the area of the cursor - need_to_draw_cursor = true; - restore_cursor_back(); - } - }; - - auto prepare_rect = [&](const Gfx::IntRect& rect) { -#ifdef COMPOSE_DEBUG - dbg() << " -> flush opaque: " << rect; -#endif - ASSERT(!flush_rects.intersects(rect)); - ASSERT(!flush_transparent_rects.intersects(rect)); - flush_rects.add(rect); - check_restore_cursor_back(rect); - }; - - auto prepare_transparency_rect = [&](const Gfx::IntRect& rect) { -#ifdef COMPOSE_DEBUG - dbg() << " -> flush transparent: " << rect; -#endif - ASSERT(!flush_rects.intersects(rect)); - bool have_rect = false; - for (auto& r : flush_transparent_rects.rects()) { - if (r == rect) { - have_rect = true; - break; - } - } - - if (!have_rect) { - flush_transparent_rects.add(rect); - check_restore_cursor_back(rect); - } - }; - - if (!m_cursor_back_bitmap || m_invalidated_cursor) - check_restore_cursor_back(cursor_rect); - - auto back_painter = *m_back_painter; - auto temp_painter = *m_temp_painter; - - auto paint_wallpaper = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) { - // FIXME: If the wallpaper is opaque, no need to fill with color! - painter.fill_rect(rect, background_color); - if (m_wallpaper) { - if (m_wallpaper_mode == WallpaperMode::Simple) { - painter.blit(rect.location(), *m_wallpaper, rect); - } else if (m_wallpaper_mode == WallpaperMode::Center) { - Gfx::IntPoint offset { ws.size().width() / 2 - m_wallpaper->size().width() / 2, - ws.size().height() / 2 - m_wallpaper->size().height() / 2 }; - painter.blit_offset(rect.location(), *m_wallpaper, - rect, offset); - } else if (m_wallpaper_mode == WallpaperMode::Tile) { - painter.draw_tiled_bitmap(rect, *m_wallpaper); - } else if (m_wallpaper_mode == WallpaperMode::Scaled) { - float hscale = (float)m_wallpaper->size().width() / (float)ws.size().width(); - float vscale = (float)m_wallpaper->size().height() / (float)ws.size().height(); - - // TODO: this may look ugly, we should scale to a backing bitmap and then blit - painter.blit_scaled(rect, *m_wallpaper, rect, hscale, vscale); - } else { - ASSERT_NOT_REACHED(); - } - } - }; - - m_opaque_wallpaper_rects.for_each_intersected(dirty_screen_rects, [&](const Gfx::IntRect& render_rect) { -#ifdef COMPOSE_DEBUG - dbg() << " render wallpaper opaque: " << render_rect; -#endif - prepare_rect(render_rect); - paint_wallpaper(back_painter, render_rect); - return IterationDecision::Continue; - }); - - auto compose_window = [&](Window& window) -> IterationDecision { - auto frame_rect = window.frame().rect(); - if (!frame_rect.intersects(ws.rect())) - return IterationDecision::Continue; - auto frame_rects = frame_rect.shatter(window.rect()); - -#ifdef COMPOSE_DEBUG - dbg() << " window " << window.title() << " frame rect: " << frame_rect; -#endif - - RefPtr<Gfx::Bitmap> backing_store = window.backing_store(); - auto compose_window_rect = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) { - if (!window.is_fullscreen()) { - rect.for_each_intersected(frame_rects, [&](const Gfx::IntRect& intersected_rect) { - // TODO: Should optimize this to use a backing buffer - Gfx::PainterStateSaver saver(painter); - painter.add_clip_rect(intersected_rect); -#ifdef COMPOSE_DEBUG - dbg() << " render frame: " << intersected_rect; -#endif - window.frame().paint(painter); - return IterationDecision::Continue; - }); - } - - if (!backing_store) { - if (window.is_opaque()) - painter.fill_rect(window.rect().intersected(rect), wm.palette().window()); - return; - } - - // Decide where we would paint this window's backing store. - // This is subtly different from widow.rect(), because window - // size may be different from its backing store size. This - // happens when the window has been resized and the client - // has not yet attached a new backing store. In this case, - // we want to try to blit the backing store at the same place - // it was previously, and fill the rest of the window with its - // background color. - Gfx::IntRect backing_rect; - backing_rect.set_size(backing_store->size()); - switch (WindowManager::the().resize_direction_of_window(window)) { - case ResizeDirection::None: - case ResizeDirection::Right: - case ResizeDirection::Down: - case ResizeDirection::DownRight: - backing_rect.set_location(window.rect().location()); - break; - case ResizeDirection::Left: - case ResizeDirection::Up: - case ResizeDirection::UpLeft: - backing_rect.set_right_without_resize(window.rect().right()); - backing_rect.set_bottom_without_resize(window.rect().bottom()); - break; - case ResizeDirection::UpRight: - backing_rect.set_left(window.rect().left()); - backing_rect.set_bottom_without_resize(window.rect().bottom()); - break; - case ResizeDirection::DownLeft: - backing_rect.set_right_without_resize(window.rect().right()); - backing_rect.set_top(window.rect().top()); - break; - } - - Gfx::IntRect dirty_rect_in_backing_coordinates = rect.intersected(window.rect()) - .intersected(backing_rect) - .translated(-backing_rect.location()); - - if (dirty_rect_in_backing_coordinates.is_empty()) - return; - auto dst = backing_rect.location().translated(dirty_rect_in_backing_coordinates.location()); - - if (window.client() && window.client()->is_unresponsive()) { - painter.blit_filtered(dst, *backing_store, dirty_rect_in_backing_coordinates, [](Color src) { - return src.to_grayscale().darkened(0.75f); - }); - } else { - painter.blit(dst, *backing_store, dirty_rect_in_backing_coordinates, window.opacity()); - } - - if (window.is_opaque()) { - for (auto background_rect : window.rect().shatter(backing_rect)) - painter.fill_rect(background_rect, wm.palette().window()); - } - }; - - auto& dirty_rects = window.dirty_rects(); -#ifdef COMPOSE_DEBUG - for (auto& dirty_rect : dirty_rects.rects()) - dbg() << " dirty: " << dirty_rect; - for (auto& r : window.opaque_rects().rects()) - dbg() << " opaque: " << r; - for (auto& r : window.transparency_rects().rects()) - dbg() << " transparent: " << r; -#endif - - // Render opaque portions directly to the back buffer - auto& opaque_rects = window.opaque_rects(); - if (!opaque_rects.is_empty()) { - opaque_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) { -#ifdef COMPOSE_DEBUG - dbg() << " render opaque: " << render_rect; -#endif - prepare_rect(render_rect); - Gfx::PainterStateSaver saver(back_painter); - back_painter.add_clip_rect(render_rect); - compose_window_rect(back_painter, render_rect); - return IterationDecision::Continue; - }); - } - - // Render the wallpaper for any transparency directly covering - // the wallpaper - auto& transparency_wallpaper_rects = window.transparency_wallpaper_rects(); - if (!transparency_wallpaper_rects.is_empty()) { - transparency_wallpaper_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) { -#ifdef COMPOSE_DEBUG - dbg() << " render wallpaper: " << render_rect; -#endif - prepare_transparency_rect(render_rect); - paint_wallpaper(temp_painter, render_rect); - return IterationDecision::Continue; - }); - } - auto& transparency_rects = window.transparency_rects(); - if (!transparency_rects.is_empty()) { - transparency_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) { -#ifdef COMPOSE_DEBUG - dbg() << " render transparent: " << render_rect; -#endif - prepare_transparency_rect(render_rect); - Gfx::PainterStateSaver saver(temp_painter); - temp_painter.add_clip_rect(render_rect); - compose_window_rect(temp_painter, render_rect); - return IterationDecision::Continue; - }); - } - return IterationDecision::Continue; - }; - - // Paint the window stack. - if (m_invalidated_window) { - if (auto* fullscreen_window = wm.active_fullscreen_window()) { - compose_window(*fullscreen_window); - } else { - wm.for_each_visible_window_from_back_to_front([&](Window& window) { - compose_window(window); - window.clear_dirty_rects(); - return IterationDecision::Continue; - }); - } - - // Check that there are no overlapping transparent and opaque flush rectangles - ASSERT(![&]() { - for (auto& rect_transparent : flush_transparent_rects.rects()) { - for (auto& rect_opaque : flush_rects.rects()) { - if (rect_opaque.intersects(rect_transparent)) { - dbg() << "Transparent rect " << rect_transparent << " overlaps opaque rect: " << rect_opaque << ": " << rect_opaque.intersected(rect_transparent); - return true; - } - } - } - return false; - }()); - - // Copy anything rendered to the temporary buffer to the back buffer - for (auto& rect : flush_transparent_rects.rects()) - back_painter.blit(rect.location(), *m_temp_bitmap, rect); - - Gfx::IntRect geometry_label_damage_rect; - if (draw_geometry_label(geometry_label_damage_rect)) - flush_special_rects.add(geometry_label_damage_rect); - } - - m_invalidated_any = false; - m_invalidated_window = false; - m_invalidated_cursor = false; - - if (wm.dnd_client()) { - auto dnd_rect = wm.dnd_rect(); - - // TODO: render once into a backing bitmap, then just blit... - auto render_dnd = [&]() { - back_painter.fill_rect(dnd_rect, wm.palette().selection().with_alpha(200)); - back_painter.draw_rect(dnd_rect, wm.palette().selection()); - if (!wm.dnd_text().is_empty()) { - auto text_rect = dnd_rect; - if (wm.dnd_bitmap()) - text_rect.move_by(wm.dnd_bitmap()->width() + 8, 0); - back_painter.draw_text(text_rect, wm.dnd_text(), Gfx::TextAlignment::CenterLeft, wm.palette().selection_text()); - } - if (wm.dnd_bitmap()) { - back_painter.blit(dnd_rect.top_left().translated(4, 4), *wm.dnd_bitmap(), wm.dnd_bitmap()->rect()); - } - }; - - dirty_screen_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) { - Gfx::PainterStateSaver saver(back_painter); - back_painter.add_clip_rect(render_rect); - render_dnd(); - return IterationDecision::Continue; - }); - flush_transparent_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) { - Gfx::PainterStateSaver saver(back_painter); - back_painter.add_clip_rect(render_rect); - render_dnd(); - return IterationDecision::Continue; - }); - m_last_dnd_rect = dnd_rect; - } else { - if (!m_last_dnd_rect.is_empty()) { - invalidate_screen(m_last_dnd_rect); - m_last_dnd_rect = {}; - } - } - - run_animations(flush_special_rects); - - if (need_to_draw_cursor) { - flush_rects.add(cursor_rect); - if (cursor_rect != m_last_cursor_rect) - flush_rects.add(m_last_cursor_rect); - draw_cursor(cursor_rect); - } - - if (m_flash_flush) { - for (auto& rect : flush_rects.rects()) - m_front_painter->fill_rect(rect, Color::Yellow); - } - - if (m_screen_can_set_buffer) - flip_buffers(); - - for (auto& rect : flush_rects.rects()) - flush(rect); - for (auto& rect : flush_transparent_rects.rects()) - flush(rect); - for (auto& rect : flush_special_rects.rects()) - flush(rect); -} - -void Compositor::flush(const Gfx::IntRect& a_rect) -{ - auto rect = Gfx::IntRect::intersection(a_rect, Screen::the().rect()); - - Gfx::RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x(); - Gfx::RGBA32* back_ptr = m_back_bitmap->scanline(rect.y()) + rect.x(); - size_t pitch = m_back_bitmap->pitch(); - - // NOTE: The meaning of a flush depends on whether we can flip buffers or not. - // - // If flipping is supported, flushing means that we've flipped, and now we - // copy the changed bits from the front buffer to the back buffer, to keep - // them in sync. - // - // If flipping is not supported, flushing means that we copy the changed - // rects from the backing bitmap to the display framebuffer. - - Gfx::RGBA32* to_ptr; - const Gfx::RGBA32* from_ptr; - - if (m_screen_can_set_buffer) { - to_ptr = back_ptr; - from_ptr = front_ptr; - } else { - to_ptr = front_ptr; - from_ptr = back_ptr; - } - - for (int y = 0; y < rect.height(); ++y) { - fast_u32_copy(to_ptr, from_ptr, rect.width()); - from_ptr = (const Gfx::RGBA32*)((const u8*)from_ptr + pitch); - to_ptr = (Gfx::RGBA32*)((u8*)to_ptr + pitch); - } -} - -void Compositor::invalidate_screen() -{ - invalidate_screen(Screen::the().rect()); -} - -void Compositor::invalidate_screen(const Gfx::IntRect& screen_rect) -{ - m_dirty_screen_rects.add(screen_rect.intersected(Screen::the().rect())); - - if (m_invalidated_any) - return; - - m_invalidated_any = true; - m_invalidated_window = true; - start_compose_async_timer(); -} - -void Compositor::invalidate_window() -{ - if (m_invalidated_window) - return; - m_invalidated_window = true; - m_invalidated_any = true; - - start_compose_async_timer(); -} - -void Compositor::start_compose_async_timer() -{ - // We delay composition by a timer interval, but to not affect latency too - // much, if a pending compose is not already scheduled, we also schedule an - // immediate compose the next spin of the event loop. - if (!m_compose_timer->is_active()) { - m_compose_timer->start(); - m_immediate_compose_timer->start(); - } -} - -bool Compositor::set_background_color(const String& background_color) -{ - auto color = Color::from_string(background_color); - if (!color.has_value()) - return false; - - m_custom_background_color = color; - - auto& wm = WindowManager::the(); - wm.config()->write_entry("Background", "Color", background_color); - bool ret_val = wm.config()->sync(); - - if (ret_val) - Compositor::invalidate_screen(); - - return ret_val; -} - -bool Compositor::set_wallpaper_mode(const String& mode) -{ - auto& wm = WindowManager::the(); - wm.config()->write_entry("Background", "Mode", mode); - bool ret_val = wm.config()->sync(); - - if (ret_val) { - m_wallpaper_mode = mode_to_enum(mode); - Compositor::invalidate_screen(); - } - - return ret_val; -} - -bool Compositor::set_wallpaper(const String& path, Function<void(bool)>&& callback) -{ - LibThread::BackgroundAction<RefPtr<Gfx::Bitmap>>::create( - [path] { - return Gfx::Bitmap::load_from_file(path); - }, - - [this, path, callback = move(callback)](RefPtr<Gfx::Bitmap> bitmap) { - m_wallpaper_path = path; - m_wallpaper = move(bitmap); - invalidate_screen(); - callback(true); - }); - return true; -} - -void Compositor::flip_buffers() -{ - ASSERT(m_screen_can_set_buffer); - swap(m_front_bitmap, m_back_bitmap); - swap(m_front_painter, m_back_painter); - Screen::the().set_buffer(m_buffers_are_flipped ? 0 : 1); - m_buffers_are_flipped = !m_buffers_are_flipped; -} - -void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects) -{ - static const int minimize_animation_steps = 10; - auto& painter = *m_back_painter; - Gfx::PainterStateSaver saver(painter); - painter.set_draw_op(Gfx::Painter::DrawOp::Invert); - - WindowManager::the().for_each_window([&](Window& window) { - if (window.in_minimize_animation()) { - int animation_index = window.minimize_animation_index(); - - auto from_rect = window.is_minimized() ? window.frame().rect() : window.taskbar_rect(); - auto to_rect = window.is_minimized() ? window.taskbar_rect() : window.frame().rect(); - - float x_delta_per_step = (float)(from_rect.x() - to_rect.x()) / minimize_animation_steps; - float y_delta_per_step = (float)(from_rect.y() - to_rect.y()) / minimize_animation_steps; - float width_delta_per_step = (float)(from_rect.width() - to_rect.width()) / minimize_animation_steps; - float height_delta_per_step = (float)(from_rect.height() - to_rect.height()) / minimize_animation_steps; - - Gfx::IntRect rect { - from_rect.x() - (int)(x_delta_per_step * animation_index), - from_rect.y() - (int)(y_delta_per_step * animation_index), - from_rect.width() - (int)(width_delta_per_step * animation_index), - from_rect.height() - (int)(height_delta_per_step * animation_index) - }; - -#ifdef MINIMIZE_ANIMATION_DEBUG - dbg() << "Minimize animation from " << from_rect << " to " << to_rect << " frame# " << animation_index << " " << rect; -#endif - - painter.draw_rect(rect, Color::Transparent); // Color doesn't matter, we draw inverted - flush_rects.add(rect); - invalidate_screen(rect); - - window.step_minimize_animation(); - if (window.minimize_animation_index() >= minimize_animation_steps) - window.end_minimize_animation(); - } - return IterationDecision::Continue; - }); -} - -bool Compositor::set_resolution(int desired_width, int desired_height) -{ - auto screen_rect = Screen::the().rect(); - if (screen_rect.width() == desired_width && screen_rect.height() == desired_height) - return true; - - // Make sure it's impossible to set an invalid resolution - if (!(desired_width >= 640 && desired_height >= 480)) { - dbg() << "Compositor: Tried to set invalid resolution: " << desired_width << "x" << desired_height; - return false; - } - bool success = Screen::the().set_resolution(desired_width, desired_height); - init_bitmaps(); - invalidate_occlusions(); - compose(); - return success; -} - -Gfx::IntRect Compositor::current_cursor_rect() const -{ - auto& wm = WindowManager::the(); - auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor(); - return { Screen::the().cursor_location().translated(-current_cursor.params().hotspot()), current_cursor.size() }; -} - -void Compositor::invalidate_cursor(bool compose_immediately) -{ - if (m_invalidated_cursor) - return; - m_invalidated_cursor = true; - m_invalidated_any = true; - - if (compose_immediately) - compose(); - else - start_compose_async_timer(); -} - -bool Compositor::draw_geometry_label(Gfx::IntRect& geometry_label_damage_rect) -{ - auto& wm = WindowManager::the(); - auto* window_being_moved_or_resized = wm.m_move_window ? wm.m_move_window.ptr() : (wm.m_resize_window ? wm.m_resize_window.ptr() : nullptr); - if (!window_being_moved_or_resized) { - m_last_geometry_label_damage_rect = {}; - return false; - } - auto geometry_string = window_being_moved_or_resized->rect().to_string(); - if (!window_being_moved_or_resized->size_increment().is_null()) { - int width_steps = (window_being_moved_or_resized->width() - window_being_moved_or_resized->base_size().width()) / window_being_moved_or_resized->size_increment().width(); - int height_steps = (window_being_moved_or_resized->height() - window_being_moved_or_resized->base_size().height()) / window_being_moved_or_resized->size_increment().height(); - geometry_string = String::format("%s (%dx%d)", geometry_string.characters(), width_steps, height_steps); - } - auto geometry_label_rect = Gfx::IntRect { 0, 0, wm.font().width(geometry_string) + 16, wm.font().glyph_height() + 10 }; - geometry_label_rect.center_within(window_being_moved_or_resized->rect()); - auto& back_painter = *m_back_painter; - back_painter.fill_rect(geometry_label_rect.translated(1, 1), Color(Color::Black).with_alpha(80)); - Gfx::StylePainter::paint_button(back_painter, geometry_label_rect.translated(-1, -1), wm.palette(), Gfx::ButtonStyle::Normal, false); - back_painter.draw_text(geometry_label_rect.translated(-1, -1), geometry_string, Gfx::TextAlignment::Center, wm.palette().window_text()); - - geometry_label_damage_rect = geometry_label_rect.inflated(2, 2); - m_last_geometry_label_damage_rect = geometry_label_damage_rect; - return true; -} - -void Compositor::change_cursor(const Cursor* cursor) -{ - if (m_current_cursor == cursor) - return; - m_current_cursor = cursor; - m_current_cursor_frame = 0; - if (m_cursor_timer) { - m_cursor_timer->stop(); - m_cursor_timer = nullptr; - } - if (cursor && cursor->params().frames() > 1 && cursor->params().frame_ms() != 0) { - m_cursor_timer = add<Core::Timer>( - cursor->params().frame_ms(), [this, cursor] { - if (m_current_cursor != cursor) - return; - auto frames = cursor->params().frames(); - if (++m_current_cursor_frame >= frames) - m_current_cursor_frame = 0; - invalidate_cursor(true); - }); - } -} - -void Compositor::draw_cursor(const Gfx::IntRect& cursor_rect) -{ - auto& wm = WindowManager::the(); - - if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size()) { - m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, cursor_rect.size()); - m_cursor_back_painter = make<Gfx::Painter>(*m_cursor_back_bitmap); - } - - auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor(); - m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, current_cursor.rect().translated(cursor_rect.location()).intersected(Screen::the().rect())); - auto& back_painter = *m_back_painter; - back_painter.blit(cursor_rect.location(), current_cursor.bitmap(), current_cursor.source_rect(m_current_cursor_frame)); - - m_last_cursor_rect = cursor_rect; -} - -void Compositor::restore_cursor_back() -{ - if (!m_cursor_back_bitmap) - return; - - m_back_painter->blit(m_last_cursor_rect.location().constrained(Screen::the().rect()), *m_cursor_back_bitmap, { { 0, 0 }, m_last_cursor_rect.intersected(Screen::the().rect()).size() }); -} - -void Compositor::notify_display_links() -{ - ClientConnection::for_each_client([](auto& client) { - client.notify_display_link({}); - }); -} - -void Compositor::increment_display_link_count(Badge<ClientConnection>) -{ - ++m_display_link_count; - if (m_display_link_count == 1) - m_display_link_notify_timer->start(); -} - -void Compositor::decrement_display_link_count(Badge<ClientConnection>) -{ - ASSERT(m_display_link_count); - --m_display_link_count; - if (!m_display_link_count) - m_display_link_notify_timer->stop(); -} - -bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_window, const Gfx::IntRect& rect) -{ - bool found_containing_window = false; - bool checking = false; - WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& window) { - if (&window == &a_window) { - checking = true; - return IterationDecision::Continue; - } - if (!checking) - return IterationDecision::Continue; - if (!window.is_visible()) - return IterationDecision::Continue; - if (window.is_minimized()) - return IterationDecision::Continue; - if (!window.is_opaque()) - return IterationDecision::Continue; - if (window.frame().rect().contains(rect)) { - found_containing_window = true; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - return found_containing_window; -}; - -void Compositor::recompute_occlusions() -{ - auto& wm = WindowManager::the(); - wm.for_each_visible_window_from_back_to_front([&](Window& window) { - if (wm.m_switcher.is_visible()) { - window.set_occluded(false); - } else { - if (any_opaque_window_above_this_one_contains_rect(window, window.frame().rect())) - window.set_occluded(true); - else - window.set_occluded(false); - } - return IterationDecision::Continue; - }); - -#ifdef OCCLUSIONS_DEBUG - dbgln("OCCLUSIONS:"); -#endif - - auto screen_rect = Screen::the().rect(); - - if (auto* fullscreen_window = wm.active_fullscreen_window()) { - WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& w) { - auto& visible_opaque = w.opaque_rects(); - auto& transparency_rects = w.transparency_rects(); - auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects(); - if (&w == fullscreen_window) { - if (w.is_opaque()) { - visible_opaque = screen_rect; - transparency_rects.clear(); - transparency_wallpaper_rects.clear(); - } else { - visible_opaque.clear(); - transparency_rects = screen_rect; - transparency_wallpaper_rects = screen_rect; - } - } else { - visible_opaque.clear(); - transparency_rects.clear(); - transparency_wallpaper_rects.clear(); - } - return IterationDecision::Continue; - }); - - m_opaque_wallpaper_rects.clear(); - } else { - Gfx::DisjointRectSet visible_rects(screen_rect); - bool have_transparent = false; - WindowManager::the().for_each_visible_window_from_front_to_back([&](Window& w) { - auto window_frame_rect = w.frame().rect().intersected(screen_rect); - w.transparency_wallpaper_rects().clear(); - auto& visible_opaque = w.opaque_rects(); - auto& transparency_rects = w.transparency_rects(); - if (w.is_minimized() || window_frame_rect.is_empty()) { - visible_opaque.clear(); - transparency_rects.clear(); - return IterationDecision::Continue; - } - - Gfx::DisjointRectSet opaque_covering; - if (w.is_opaque()) { - visible_opaque = visible_rects.intersected(window_frame_rect); - transparency_rects.clear(); - } else { - visible_opaque.clear(); - transparency_rects = visible_rects.intersected(window_frame_rect); - } - - bool found_this_window = false; - WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& w2) { - if (!found_this_window) { - if (&w == &w2) - found_this_window = true; - return IterationDecision::Continue; - } - - if (w2.is_minimized()) - return IterationDecision::Continue; - auto window_frame_rect2 = w2.frame().rect().intersected(screen_rect); - auto covering_rect = window_frame_rect2.intersected(window_frame_rect); - if (covering_rect.is_empty()) - return IterationDecision::Continue; - - if (w2.is_opaque()) { - opaque_covering.add(covering_rect); - if (opaque_covering.contains(window_frame_rect)) { - // This window is entirely covered by another opaque window - visible_opaque.clear(); - transparency_rects.clear(); - return IterationDecision::Break; - } - - if (!visible_opaque.is_empty()) { - auto uncovered_opaque = visible_opaque.shatter(covering_rect); - visible_opaque = move(uncovered_opaque); - } - - if (!transparency_rects.is_empty()) { - auto uncovered_transparency = transparency_rects.shatter(covering_rect); - transparency_rects = move(uncovered_transparency); - } - } else { - visible_rects.for_each_intersected(covering_rect, [&](const Gfx::IntRect& intersected) { - transparency_rects.add(intersected); - if (!visible_opaque.is_empty()) { - auto uncovered_opaque = visible_opaque.shatter(intersected); - visible_opaque = move(uncovered_opaque); - } - return IterationDecision::Continue; - }); - } - - return IterationDecision::Continue; - }); - - if (!transparency_rects.is_empty()) - have_transparent = true; - - ASSERT(!visible_opaque.intersects(transparency_rects)); - - if (w.is_opaque()) { - // Determine visible area for the window below - auto visible_rects_below_window = visible_rects.shatter(window_frame_rect); - visible_rects = move(visible_rects_below_window); - } - return IterationDecision::Continue; - }); - - if (have_transparent) { - // Determine what transparent window areas need to render the wallpaper first - WindowManager::the().for_each_visible_window_from_back_to_front([&](Window& w) { - auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects(); - if (w.is_opaque() || w.is_minimized()) { - transparency_wallpaper_rects.clear(); - return IterationDecision::Continue; - } - Gfx::DisjointRectSet& transparency_rects = w.transparency_rects(); - if (transparency_rects.is_empty()) { - transparency_wallpaper_rects.clear(); - return IterationDecision::Continue; - } - - transparency_wallpaper_rects = visible_rects.intersected(transparency_rects); - - auto remaining_visible = visible_rects.shatter(transparency_wallpaper_rects); - visible_rects = move(remaining_visible); - return IterationDecision::Continue; - }); - } - - m_opaque_wallpaper_rects = move(visible_rects); - } - -#ifdef OCCLUSIONS_DEBUG - for (auto& r : m_opaque_wallpaper_rects.rects()) - dbg() << " wallpaper opaque: " << r; -#endif - - wm.for_each_visible_window_from_back_to_front([&](Window& w) { - auto window_frame_rect = w.frame().rect().intersected(screen_rect); - if (w.is_minimized() || window_frame_rect.is_empty()) - return IterationDecision::Continue; - -#ifdef OCCLUSIONS_DEBUG - dbg() << " Window " << w.title() << " frame rect: " << window_frame_rect; - for (auto& r : w.opaque_rects().rects()) - dbg() << " opaque: " << r; - for (auto& r : w.transparency_wallpaper_rects().rects()) - dbg() << " transparent wallpaper: " << r; - for (auto& r : w.transparency_rects().rects()) - dbg() << " transparent: " << r; -#endif - ASSERT(!w.opaque_rects().intersects(m_opaque_wallpaper_rects)); - ASSERT(!w.transparency_rects().intersects(m_opaque_wallpaper_rects)); - ASSERT(!w.transparency_wallpaper_rects().intersects(m_opaque_wallpaper_rects)); - return IterationDecision::Continue; - }); -} - -} diff --git a/Services/WindowServer/Compositor.h b/Services/WindowServer/Compositor.h deleted file mode 100644 index 1e6a3e2014..0000000000 --- a/Services/WindowServer/Compositor.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/OwnPtr.h> -#include <AK/RefPtr.h> -#include <LibCore/Object.h> -#include <LibGfx/Color.h> -#include <LibGfx/DisjointRectSet.h> - -namespace WindowServer { - -class ClientConnection; -class Cursor; -class Window; -class WindowManager; - -enum class WallpaperMode { - Simple, - Tile, - Center, - Scaled, - Unchecked -}; - -class Compositor final : public Core::Object { - C_OBJECT(Compositor) -public: - static Compositor& the(); - - void compose(); - void invalidate_window(); - void invalidate_screen(); - void invalidate_screen(const Gfx::IntRect&); - - bool set_resolution(int desired_width, int desired_height); - - bool set_background_color(const String& background_color); - - bool set_wallpaper_mode(const String& mode); - - bool set_wallpaper(const String& path, Function<void(bool)>&& callback); - String wallpaper_path() const { return m_wallpaper_path; } - - void invalidate_cursor(bool = false); - Gfx::IntRect current_cursor_rect() const; - - void increment_display_link_count(Badge<ClientConnection>); - void decrement_display_link_count(Badge<ClientConnection>); - - void invalidate_occlusions() { m_occlusions_dirty = true; } - - void did_construct_window_manager(Badge<WindowManager>); - -private: - Compositor(); - void init_bitmaps(); - void flip_buffers(); - void flush(const Gfx::IntRect&); - void draw_menubar(); - void run_animations(Gfx::DisjointRectSet&); - void notify_display_links(); - void start_compose_async_timer(); - void recompute_occlusions(); - bool any_opaque_window_above_this_one_contains_rect(const Window&, const Gfx::IntRect&); - void change_cursor(const Cursor*); - void draw_cursor(const Gfx::IntRect&); - void restore_cursor_back(); - bool draw_geometry_label(Gfx::IntRect&); - - RefPtr<Core::Timer> m_compose_timer; - RefPtr<Core::Timer> m_immediate_compose_timer; - bool m_flash_flush { false }; - bool m_buffers_are_flipped { false }; - bool m_screen_can_set_buffer { false }; - bool m_occlusions_dirty { true }; - bool m_invalidated_any { true }; - bool m_invalidated_window { false }; - bool m_invalidated_cursor { false }; - - RefPtr<Gfx::Bitmap> m_front_bitmap; - RefPtr<Gfx::Bitmap> m_back_bitmap; - RefPtr<Gfx::Bitmap> m_temp_bitmap; - OwnPtr<Gfx::Painter> m_back_painter; - OwnPtr<Gfx::Painter> m_front_painter; - OwnPtr<Gfx::Painter> m_temp_painter; - - Gfx::DisjointRectSet m_dirty_screen_rects; - Gfx::DisjointRectSet m_opaque_wallpaper_rects; - - RefPtr<Gfx::Bitmap> m_cursor_back_bitmap; - OwnPtr<Gfx::Painter> m_cursor_back_painter; - Gfx::IntRect m_last_cursor_rect; - Gfx::IntRect m_last_dnd_rect; - Gfx::IntRect m_last_geometry_label_damage_rect; - - String m_wallpaper_path { "" }; - WallpaperMode m_wallpaper_mode { WallpaperMode::Unchecked }; - RefPtr<Gfx::Bitmap> m_wallpaper; - - const Cursor* m_current_cursor { nullptr }; - unsigned m_current_cursor_frame { 0 }; - RefPtr<Core::Timer> m_cursor_timer; - - RefPtr<Core::Timer> m_display_link_notify_timer; - size_t m_display_link_count { 0 }; - - Optional<Gfx::Color> m_custom_background_color; -}; - -} diff --git a/Services/WindowServer/Cursor.cpp b/Services/WindowServer/Cursor.cpp deleted file mode 100644 index db18aa4281..0000000000 --- a/Services/WindowServer/Cursor.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/LexicalPath.h> -#include <WindowServer/Cursor.h> -#include <WindowServer/WindowManager.h> - -namespace WindowServer { - -CursorParams CursorParams::parse_from_file_name(const StringView& cursor_path, const Gfx::IntPoint& default_hotspot) -{ - LexicalPath path(cursor_path); - if (!path.is_valid()) { - dbgln("Cannot parse invalid cursor path, use default cursor params"); - return { default_hotspot }; - } - auto file_title = path.title(); - auto last_dot_in_title = StringView(file_title).find_last_of('.'); - if (!last_dot_in_title.has_value() || last_dot_in_title.value() == 0) { - // No encoded params in filename. Not an error, we'll just use defaults - return { default_hotspot }; - } - auto params_str = file_title.substring_view(last_dot_in_title.value() + 1); - - CursorParams params(default_hotspot); - for (size_t i = 0; i + 1 < params_str.length();) { - auto property = params_str[i++]; - - auto value = [&]() -> Optional<size_t> { - size_t k = i; - while (k < params_str.length()) { - auto ch = params_str[k]; - if (ch < '0' || ch > '9') - break; - k++; - } - if (k == i) - return {}; - auto parsed_number = params_str.substring_view(i, k - i).to_uint(); - if (!parsed_number.has_value()) - return {}; - i = k; - return parsed_number.value(); - }(); - if (!value.has_value()) { - dbg() << "Failed to parse value for property '" << property << "' from parsed cursor path: " << cursor_path; - return { default_hotspot }; - } - switch (property) { - case 'x': - params.m_hotspot.set_x(value.value()); - params.m_have_hotspot = true; - break; - case 'y': - params.m_hotspot.set_y(value.value()); - params.m_have_hotspot = true; - break; - case 'f': - if (value.value() > 1) - params.m_frames = value.value(); - break; - case 't': - if (value.value() >= 100 && value.value() <= 1000) - params.m_frame_ms = value.value(); - else - dbgln("Cursor frame rate outside of valid range (100-1000ms)"); - break; - default: - dbg() << "Ignore unknown property '" << property << "' with value " << value.value() << " parsed from cursor path: " << cursor_path; - return { default_hotspot }; - } - } - return params; -} - -CursorParams CursorParams::constrained(const Gfx::Bitmap& bitmap) const -{ - CursorParams params(*this); - auto rect = bitmap.rect(); - if (params.m_frames > 1) { - if (rect.width() % params.m_frames == 0) { - rect.set_width(rect.width() / (int)params.m_frames); - } else { - dbg() << "Cannot divide cursor dimensions " << rect << " into " << params.m_frames << " frames"; - params.m_frames = 1; - } - } - if (params.m_have_hotspot) - params.m_hotspot = params.m_hotspot.constrained(rect); - else - params.m_hotspot = rect.center(); - return params; -} - -Cursor::Cursor(NonnullRefPtr<Gfx::Bitmap>&& bitmap, const CursorParams& cursor_params) - : m_bitmap(move(bitmap)) - , m_params(cursor_params.constrained(*m_bitmap)) - , m_rect(m_bitmap->rect()) -{ - if (m_params.frames() > 1) { - ASSERT(m_rect.width() % m_params.frames() == 0); - m_rect.set_width(m_rect.width() / m_params.frames()); - } -} - -Cursor::~Cursor() -{ -} - -NonnullRefPtr<Cursor> Cursor::create(NonnullRefPtr<Gfx::Bitmap>&& bitmap) -{ - auto hotspot = bitmap->rect().center(); - return adopt(*new Cursor(move(bitmap), CursorParams(hotspot))); -} - -NonnullRefPtr<Cursor> Cursor::create(NonnullRefPtr<Gfx::Bitmap>&& bitmap, const StringView& filename) -{ - auto default_hotspot = bitmap->rect().center(); - return adopt(*new Cursor(move(bitmap), CursorParams::parse_from_file_name(filename, default_hotspot))); -} - -RefPtr<Cursor> Cursor::create(Gfx::StandardCursor standard_cursor) -{ - switch (standard_cursor) { - case Gfx::StandardCursor::None: - return nullptr; - case Gfx::StandardCursor::Hidden: - return WindowManager::the().hidden_cursor(); - case Gfx::StandardCursor::Arrow: - return WindowManager::the().arrow_cursor(); - case Gfx::StandardCursor::Crosshair: - return WindowManager::the().crosshair_cursor(); - case Gfx::StandardCursor::IBeam: - return WindowManager::the().i_beam_cursor(); - case Gfx::StandardCursor::ResizeHorizontal: - return WindowManager::the().resize_horizontally_cursor(); - case Gfx::StandardCursor::ResizeVertical: - return WindowManager::the().resize_vertically_cursor(); - case Gfx::StandardCursor::ResizeDiagonalTLBR: - return WindowManager::the().resize_diagonally_tlbr_cursor(); - case Gfx::StandardCursor::ResizeDiagonalBLTR: - return WindowManager::the().resize_diagonally_bltr_cursor(); - case Gfx::StandardCursor::ResizeColumn: - return WindowManager::the().resize_column_cursor(); - case Gfx::StandardCursor::ResizeRow: - return WindowManager::the().resize_row_cursor(); - case Gfx::StandardCursor::Hand: - return WindowManager::the().hand_cursor(); - case Gfx::StandardCursor::Help: - return WindowManager::the().help_cursor(); - case Gfx::StandardCursor::Drag: - return WindowManager::the().drag_cursor(); - case Gfx::StandardCursor::Move: - return WindowManager::the().move_cursor(); - case Gfx::StandardCursor::Wait: - return WindowManager::the().wait_cursor(); - default: - ASSERT_NOT_REACHED(); - } -} - -} diff --git a/Services/WindowServer/Cursor.h b/Services/WindowServer/Cursor.h deleted file mode 100644 index efe014a735..0000000000 --- a/Services/WindowServer/Cursor.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <LibGfx/Bitmap.h> -#include <LibGfx/StandardCursor.h> - -namespace WindowServer { - -class CursorParams { -public: - static CursorParams parse_from_file_name(const StringView&, const Gfx::IntPoint&); - CursorParams(const Gfx::IntPoint& hotspot) - : m_hotspot(hotspot) - { - } - CursorParams constrained(const Gfx::Bitmap&) const; - - const Gfx::IntPoint& hotspot() const { return m_hotspot; } - unsigned frames() const { return m_frames; } - unsigned frame_ms() const { return m_frame_ms; } - -private: - CursorParams() = default; - Gfx::IntPoint m_hotspot; - unsigned m_frames { 1 }; - unsigned m_frame_ms { 0 }; - bool m_have_hotspot { false }; -}; - -class Cursor : public RefCounted<Cursor> { -public: - static NonnullRefPtr<Cursor> create(NonnullRefPtr<Gfx::Bitmap>&&, const StringView&); - static NonnullRefPtr<Cursor> create(NonnullRefPtr<Gfx::Bitmap>&&); - static RefPtr<Cursor> create(Gfx::StandardCursor); - ~Cursor(); - - const CursorParams& params() const { return m_params; } - const Gfx::Bitmap& bitmap() const { return *m_bitmap; } - - Gfx::IntRect source_rect(unsigned frame) const - { - return m_rect.translated(frame * m_rect.width(), 0); - } - - Gfx::IntRect rect() const { return m_rect; } - Gfx::IntSize size() const { return m_rect.size(); } - -private: - Cursor(NonnullRefPtr<Gfx::Bitmap>&&, const CursorParams&); - - RefPtr<Gfx::Bitmap> m_bitmap; - CursorParams m_params; - Gfx::IntRect m_rect; -}; - -} diff --git a/Services/WindowServer/Event.h b/Services/WindowServer/Event.h deleted file mode 100644 index e82df2d4c9..0000000000 --- a/Services/WindowServer/Event.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/String.h> -#include <Kernel/API/KeyCode.h> -#include <LibCore/Event.h> -#include <LibCore/MimeData.h> -#include <LibGfx/Rect.h> -#include <WindowServer/Cursor.h> -#include <WindowServer/WindowType.h> - -namespace WindowServer { - -class Event : public Core::Event { -public: - enum Type { - Invalid = 3000, - MouseMove, - MouseDown, - MouseDoubleClick, - MouseUp, - MouseWheel, - WindowEntered, - WindowLeft, - KeyDown, - KeyUp, - WindowActivated, - WindowDeactivated, - WindowInputEntered, - WindowInputLeft, - WindowCloseRequest, - WindowResized, - }; - - Event() { } - explicit Event(Type type) - : Core::Event(type) - { - } - virtual ~Event() { } - - bool is_mouse_event() const { return type() == MouseMove || type() == MouseDown || type() == MouseDoubleClick || type() == MouseUp || type() == MouseWheel; } - bool is_key_event() const { return type() == KeyUp || type() == KeyDown; } -}; - -enum class MouseButton : u8 { - None = 0, - Left = 1, - Right = 2, - Middle = 4, - Back = 8, - Forward = 16, -}; - -class KeyEvent final : public Event { -public: - KeyEvent(Type type, int key, u32 code_point, u8 modifiers, u32 scancode) - : Event(type) - , m_key(key) - , m_code_point(code_point) - , m_modifiers(modifiers) - , m_scancode(scancode) - { - } - - int key() const { return m_key; } - bool ctrl() const { return m_modifiers & Mod_Ctrl; } - bool alt() const { return m_modifiers & Mod_Alt; } - bool shift() const { return m_modifiers & Mod_Shift; } - bool logo() const { return m_modifiers & Mod_Logo; } - u8 modifiers() const { return m_modifiers; } - u32 code_point() const { return m_code_point; } - u32 scancode() const { return m_scancode; } - -private: - friend class EventLoop; - friend class Screen; - int m_key { 0 }; - u32 m_code_point { 0 }; - u8 m_modifiers { 0 }; - u32 m_scancode { 0 }; -}; - -class MouseEvent final : public Event { -public: - MouseEvent(Type type, const Gfx::IntPoint& position, unsigned buttons, MouseButton button, unsigned modifiers, int wheel_delta = 0) - : Event(type) - , m_position(position) - , m_buttons(buttons) - , m_button(button) - , m_modifiers(modifiers) - , m_wheel_delta(wheel_delta) - { - } - - const Gfx::IntPoint& position() const { return m_position; } - int x() const { return m_position.x(); } - int y() const { return m_position.y(); } - MouseButton button() const { return m_button; } - unsigned buttons() const { return m_buttons; } - unsigned modifiers() const { return m_modifiers; } - int wheel_delta() const { return m_wheel_delta; } - bool is_drag() const { return m_drag; } - - Vector<String> mime_types() const - { - if (!m_mime_data) - return {}; - return m_mime_data->formats(); - } - - void set_drag(bool b) { m_drag = b; } - void set_mime_data(const Core::MimeData& mime_data) { m_mime_data = mime_data; } - - MouseEvent translated(const Gfx::IntPoint& delta) const { return MouseEvent((Type)type(), m_position.translated(delta), m_buttons, m_button, m_modifiers, m_wheel_delta); } - -private: - Gfx::IntPoint m_position; - unsigned m_buttons { 0 }; - MouseButton m_button { MouseButton::None }; - unsigned m_modifiers { 0 }; - int m_wheel_delta { 0 }; - bool m_drag { false }; - RefPtr<const Core::MimeData> m_mime_data; -}; - -class ResizeEvent final : public Event { -public: - ResizeEvent(const Gfx::IntRect& rect) - : Event(Event::WindowResized) - , m_rect(rect) - { - } - - const Gfx::IntRect& rect() const { return m_rect; } - -private: - Gfx::IntRect m_rect; -}; - -} diff --git a/Services/WindowServer/EventLoop.cpp b/Services/WindowServer/EventLoop.cpp deleted file mode 100644 index 21dae58823..0000000000 --- a/Services/WindowServer/EventLoop.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <Kernel/API/MousePacket.h> -#include <LibCore/LocalSocket.h> -#include <LibCore/Object.h> -#include <WindowServer/ClientConnection.h> -#include <WindowServer/Cursor.h> -#include <WindowServer/Event.h> -#include <WindowServer/EventLoop.h> -#include <WindowServer/Screen.h> -#include <WindowServer/WindowManager.h> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <sys/select.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <time.h> -#include <unistd.h> - -//#define WSMESSAGELOOP_DEBUG - -namespace WindowServer { - -EventLoop::EventLoop() - : m_server(Core::LocalServer::construct()) -{ - m_keyboard_fd = open("/dev/keyboard", O_RDONLY | O_NONBLOCK | O_CLOEXEC); - m_mouse_fd = open("/dev/mouse", O_RDONLY | O_NONBLOCK | O_CLOEXEC); - - bool ok = m_server->take_over_from_system_server(); - ASSERT(ok); - - m_server->on_ready_to_accept = [this] { - auto client_socket = m_server->accept(); - if (!client_socket) { - dbgln("WindowServer: accept failed."); - return; - } - static int s_next_client_id = 0; - int client_id = ++s_next_client_id; - IPC::new_client_connection<ClientConnection>(client_socket.release_nonnull(), client_id); - }; - - ASSERT(m_keyboard_fd >= 0); - ASSERT(m_mouse_fd >= 0); - - m_keyboard_notifier = Core::Notifier::construct(m_keyboard_fd, Core::Notifier::Read); - m_keyboard_notifier->on_ready_to_read = [this] { drain_keyboard(); }; - - m_mouse_notifier = Core::Notifier::construct(m_mouse_fd, Core::Notifier::Read); - m_mouse_notifier->on_ready_to_read = [this] { drain_mouse(); }; -} - -EventLoop::~EventLoop() -{ -} - -void EventLoop::drain_mouse() -{ - auto& screen = Screen::the(); - MousePacket state; - state.buttons = screen.mouse_button_state(); - unsigned buttons = state.buttons; - MousePacket packets[32]; - - ssize_t nread = read(m_mouse_fd, &packets, sizeof(packets)); - if (nread < 0) { - perror("EventLoop::drain_mouse read"); - return; - } - size_t npackets = nread / sizeof(MousePacket); - if (!npackets) - return; - for (size_t i = 0; i < npackets; ++i) { - auto& packet = packets[i]; -#ifdef WSMESSAGELOOP_DEBUG - dbgln("EventLoop: Mouse X {}, Y {}, Z {}, relative={}", packet.x, packet.y, packet.z, packet.is_relative); -#endif - buttons = packet.buttons; - - state.is_relative = packet.is_relative; - if (packet.is_relative) { - state.x += packet.x; - state.y -= packet.y; - state.z += packet.z; - } else { - state.x = packet.x; - state.y = packet.y; - state.z += packet.z; - } - - if (buttons != state.buttons) { - state.buttons = buttons; -#ifdef WSMESSAGELOOP_DEBUG - dbgln("EventLoop: Mouse Button Event"); -#endif - screen.on_receive_mouse_data(state); - if (state.is_relative) { - state.x = 0; - state.y = 0; - state.z = 0; - } - } - } - if (state.is_relative && (state.x || state.y || state.z)) - screen.on_receive_mouse_data(state); - if (!state.is_relative) - screen.on_receive_mouse_data(state); -} - -void EventLoop::drain_keyboard() -{ - auto& screen = Screen::the(); - for (;;) { - ::KeyEvent event; - ssize_t nread = read(m_keyboard_fd, (u8*)&event, sizeof(::KeyEvent)); - if (nread == 0) - break; - ASSERT(nread == sizeof(::KeyEvent)); - screen.on_receive_keyboard_data(event); - } -} - -} diff --git a/Services/WindowServer/EventLoop.h b/Services/WindowServer/EventLoop.h deleted file mode 100644 index b476b5e639..0000000000 --- a/Services/WindowServer/EventLoop.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/ByteBuffer.h> -#include <LibCore/EventLoop.h> -#include <LibCore/LocalServer.h> -#include <LibCore/Notifier.h> - -namespace WindowServer { - -class ClientConnection; - -class EventLoop { -public: - EventLoop(); - virtual ~EventLoop(); - - int exec() { return m_event_loop.exec(); } - -private: - void drain_mouse(); - void drain_keyboard(); - - Core::EventLoop m_event_loop; - int m_keyboard_fd { -1 }; - RefPtr<Core::Notifier> m_keyboard_notifier; - int m_mouse_fd { -1 }; - RefPtr<Core::Notifier> m_mouse_notifier; - RefPtr<Core::LocalServer> m_server; -}; - -} diff --git a/Services/WindowServer/Menu.cpp b/Services/WindowServer/Menu.cpp deleted file mode 100644 index 4d204406c2..0000000000 --- a/Services/WindowServer/Menu.cpp +++ /dev/null @@ -1,568 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2020, Shannon Booth <shannon.ml.booth@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Menu.h" -#include "Event.h" -#include "MenuItem.h" -#include "MenuManager.h" -#include "Screen.h" -#include "Window.h" -#include "WindowManager.h" -#include <LibGfx/Bitmap.h> -#include <LibGfx/CharacterBitmap.h> -#include <LibGfx/Font.h> -#include <LibGfx/Painter.h> -#include <LibGfx/StylePainter.h> -#include <LibGfx/Triangle.h> -#include <WindowServer/ClientConnection.h> -#include <WindowServer/WindowClientEndpoint.h> - -namespace WindowServer { - -Menu::Menu(ClientConnection* client, int menu_id, const String& name) - : Core::Object(client) - , m_client(client) - , m_menu_id(menu_id) - , m_name(move(name)) -{ -} - -Menu::~Menu() -{ -} - -void Menu::set_title_font(const Gfx::Font& font) -{ - m_title_font = &font; -} - -const Gfx::Font& Menu::title_font() const -{ - return *m_title_font; -} - -const Gfx::Font& Menu::font() const -{ - return Gfx::FontDatabase::default_font(); -} - -static const char* s_checked_bitmap_data = { - " " - " # " - " ## " - " ### " - " ## ### " - " ##### " - " ### " - " # " - " " -}; - -static const char* s_submenu_arrow_bitmap_data = { - " " - " # " - " ## " - " ### " - " #### " - " ### " - " ## " - " # " - " " -}; - -static Gfx::CharacterBitmap* s_checked_bitmap; -static const int s_checked_bitmap_width = 9; -static const int s_checked_bitmap_height = 9; -static const int s_submenu_arrow_bitmap_width = 9; -static const int s_submenu_arrow_bitmap_height = 9; -static const int s_item_icon_width = 16; -static const int s_stripe_width = 23; - -int Menu::content_width() const -{ - int widest_text = 0; - int widest_shortcut = 0; - for (auto& item : m_items) { - if (item.type() != MenuItem::Text) - continue; - auto& use_font = item.is_default() ? Gfx::FontDatabase::default_bold_font() : font(); - int text_width = use_font.width(item.text()); - if (!item.shortcut_text().is_empty()) { - int shortcut_width = use_font.width(item.shortcut_text()); - widest_shortcut = max(shortcut_width, widest_shortcut); - } - widest_text = max(widest_text, text_width); - } - - int widest_item = widest_text + s_stripe_width; - if (widest_shortcut) - widest_item += padding_between_text_and_shortcut() + widest_shortcut; - - return max(widest_item, rect_in_menubar().width()) + horizontal_padding() + frame_thickness() * 2; -} - -void Menu::redraw() -{ - if (!menu_window()) - return; - draw(); - menu_window()->invalidate(); -} - -Window& Menu::ensure_menu_window() -{ - if (m_menu_window) - return *m_menu_window; - - int width = this->content_width(); - - Gfx::IntPoint next_item_location(frame_thickness(), frame_thickness()); - for (auto& item : m_items) { - int height = 0; - if (item.type() == MenuItem::Text) - height = item_height(); - else if (item.type() == MenuItem::Separator) - height = 8; - item.set_rect({ next_item_location, { width - frame_thickness() * 2, height } }); - next_item_location.move_by(0, height); - } - - int window_height_available = Screen::the().height() - MenuManager::the().menubar_rect().height() - frame_thickness() * 2; - int max_window_height = (window_height_available / item_height()) * item_height() + frame_thickness() * 2; - int content_height = m_items.is_empty() ? 0 : (m_items.last().rect().bottom() + 1) + frame_thickness(); - int window_height = min(max_window_height, content_height); - if (window_height < content_height) { - m_scrollable = true; - m_max_scroll_offset = item_count() - window_height / item_height() + 2; - } - - auto window = Window::construct(*this, WindowType::Menu); - window->set_rect(0, 0, width, window_height); - m_menu_window = move(window); - draw(); - - return *m_menu_window; -} - -int Menu::visible_item_count() const -{ - if (!is_scrollable()) - return m_items.size(); - ASSERT(m_menu_window); - // Make space for up/down arrow indicators - return m_menu_window->height() / item_height() - 2; -} - -void Menu::draw() -{ - auto palette = WindowManager::the().palette(); - m_theme_index_at_last_paint = MenuManager::the().theme_index(); - - ASSERT(menu_window()); - ASSERT(menu_window()->backing_store()); - Gfx::Painter painter(*menu_window()->backing_store()); - - Gfx::IntRect rect { {}, menu_window()->size() }; - Gfx::StylePainter::paint_window_frame(painter, rect, palette); - painter.fill_rect(rect.shrunken(6, 6), palette.menu_base()); - int width = this->content_width(); - - if (!s_checked_bitmap) - s_checked_bitmap = &Gfx::CharacterBitmap::create_from_ascii(s_checked_bitmap_data, s_checked_bitmap_width, s_checked_bitmap_height).leak_ref(); - - bool has_checkable_items = false; - bool has_items_with_icon = false; - for (auto& item : m_items) { - has_checkable_items = has_checkable_items | item.is_checkable(); - has_items_with_icon = has_items_with_icon | !!item.icon(); - } - - Gfx::IntRect stripe_rect { frame_thickness(), frame_thickness(), s_stripe_width, menu_window()->height() - frame_thickness() * 2 }; - painter.fill_rect(stripe_rect, palette.menu_stripe()); - painter.draw_line(stripe_rect.top_right(), stripe_rect.bottom_right(), palette.menu_stripe().darkened()); - - int visible_item_count = this->visible_item_count(); - - if (is_scrollable()) { - bool can_go_up = m_scroll_offset > 0; - bool can_go_down = m_scroll_offset < m_max_scroll_offset; - Gfx::IntRect up_indicator_rect { frame_thickness(), frame_thickness(), content_width(), item_height() }; - painter.draw_text(up_indicator_rect, "\xE2\xAC\x86", Gfx::TextAlignment::Center, can_go_up ? palette.menu_base_text() : palette.color(ColorRole::DisabledText)); - Gfx::IntRect down_indicator_rect { frame_thickness(), menu_window()->height() - item_height() - frame_thickness(), content_width(), item_height() }; - painter.draw_text(down_indicator_rect, "\xE2\xAC\x87", Gfx::TextAlignment::Center, can_go_down ? palette.menu_base_text() : palette.color(ColorRole::DisabledText)); - } - - for (int i = 0; i < visible_item_count; ++i) { - auto& item = m_items.at(m_scroll_offset + i); - if (item.type() == MenuItem::Text) { - Color text_color = palette.menu_base_text(); - if (&item == hovered_item() && item.is_enabled()) { - painter.fill_rect(item.rect(), palette.menu_selection()); - painter.draw_rect(item.rect(), palette.menu_selection().darkened()); - text_color = palette.menu_selection_text(); - } else if (!item.is_enabled()) { - text_color = Color::MidGray; - } - Gfx::IntRect text_rect = item.rect().translated(stripe_rect.width() + 6, 0); - if (item.is_checkable()) { - if (item.is_exclusive()) { - Gfx::IntRect radio_rect { item.rect().x() + 5, 0, 12, 12 }; - radio_rect.center_vertically_within(text_rect); - Gfx::StylePainter::paint_radio_button(painter, radio_rect, palette, item.is_checked(), false); - } else { - Gfx::IntRect checkmark_rect { item.rect().x() + 7, 0, s_checked_bitmap_width, s_checked_bitmap_height }; - checkmark_rect.center_vertically_within(text_rect); - Gfx::IntRect checkbox_rect = checkmark_rect.inflated(4, 4); - painter.fill_rect(checkbox_rect, palette.base()); - Gfx::StylePainter::paint_frame(painter, checkbox_rect, palette, Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2); - if (item.is_checked()) { - painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, palette.button_text()); - } - } - } else if (item.icon()) { - Gfx::IntRect icon_rect { item.rect().x() + 3, 0, s_item_icon_width, s_item_icon_width }; - icon_rect.center_vertically_within(text_rect); - - if (&item == hovered_item() && item.is_enabled()) { - auto shadow_color = palette.menu_selection().darkened(0.7f); - painter.blit_filtered(icon_rect.location().translated(1, 1), *item.icon(), item.icon()->rect(), [&shadow_color](auto) { - return shadow_color; - }); - icon_rect.move_by(-1, -1); - } - if (item.is_enabled()) - painter.blit(icon_rect.location(), *item.icon(), item.icon()->rect()); - else - painter.blit_disabled(icon_rect.location(), *item.icon(), item.icon()->rect(), palette); - } - auto& previous_font = painter.font(); - if (item.is_default()) - painter.set_font(Gfx::FontDatabase::default_bold_font()); - painter.draw_text(text_rect, item.text(), Gfx::TextAlignment::CenterLeft, text_color); - if (!item.shortcut_text().is_empty()) { - painter.draw_text(item.rect().translated(-right_padding(), 0), item.shortcut_text(), Gfx::TextAlignment::CenterRight, text_color); - } - painter.set_font(previous_font); - if (item.is_submenu()) { - static auto& submenu_arrow_bitmap = Gfx::CharacterBitmap::create_from_ascii(s_submenu_arrow_bitmap_data, s_submenu_arrow_bitmap_width, s_submenu_arrow_bitmap_height).leak_ref(); - Gfx::IntRect submenu_arrow_rect { - item.rect().right() - s_submenu_arrow_bitmap_width - 2, - 0, - s_submenu_arrow_bitmap_width, - s_submenu_arrow_bitmap_height - }; - submenu_arrow_rect.center_vertically_within(item.rect()); - painter.draw_bitmap(submenu_arrow_rect.location(), submenu_arrow_bitmap, text_color); - } - } else if (item.type() == MenuItem::Separator) { - Gfx::IntPoint p1(item.rect().translated(stripe_rect.width() + 4, 0).x(), item.rect().center().y() - 1); - Gfx::IntPoint p2(width - 7, item.rect().center().y() - 1); - painter.draw_line(p1, p2, palette.threed_shadow1()); - painter.draw_line(p1.translated(0, 1), p2.translated(0, 1), palette.threed_highlight()); - } - } -} - -MenuItem* Menu::hovered_item() const -{ - if (m_hovered_item_index == -1) - return nullptr; - return const_cast<MenuItem*>(&item(m_hovered_item_index)); -} - -void Menu::update_for_new_hovered_item(bool make_input) -{ - if (hovered_item() && hovered_item()->is_submenu()) { - ASSERT(menu_window()); - MenuManager::the().close_everyone_not_in_lineage(*hovered_item()->submenu()); - hovered_item()->submenu()->do_popup(hovered_item()->rect().top_right().translated(menu_window()->rect().location()), make_input); - } else { - MenuManager::the().close_everyone_not_in_lineage(*this); - ensure_menu_window().set_visible(true); - } - redraw(); -} - -void Menu::open_hovered_item() -{ - ASSERT(menu_window()); - ASSERT(menu_window()->is_visible()); - if (!hovered_item()) - return; - if (hovered_item()->is_enabled()) - did_activate(*hovered_item()); - clear_hovered_item(); -} - -void Menu::descend_into_submenu_at_hovered_item() -{ - ASSERT(hovered_item()); - auto submenu = hovered_item()->submenu(); - ASSERT(submenu); - MenuManager::the().open_menu(*submenu, false); - submenu->set_hovered_item(0); - ASSERT(submenu->hovered_item()->type() != MenuItem::Separator); -} - -void Menu::handle_mouse_move_event(const MouseEvent& mouse_event) -{ - ASSERT(menu_window()); - MenuManager::the().set_current_menu(this); - if (hovered_item() && hovered_item()->is_submenu()) { - - auto item = *hovered_item(); - auto submenu_top_left = item.rect().location() + Gfx::IntPoint { item.rect().width(), 0 }; - auto submenu_bottom_left = submenu_top_left + Gfx::IntPoint { 0, item.submenu()->menu_window()->height() }; - - auto safe_hover_triangle = Gfx::Triangle { m_last_position_in_hover, submenu_top_left, submenu_bottom_left }; - m_last_position_in_hover = mouse_event.position(); - - // Don't update the hovered item if mouse is moving towards a submenu - if (safe_hover_triangle.contains(mouse_event.position())) - return; - } - - int index = item_index_at(mouse_event.position()); - if (m_hovered_item_index == index) - return; - m_hovered_item_index = index; - - update_for_new_hovered_item(); - return; -} - -void Menu::event(Core::Event& event) -{ - if (event.type() == Event::MouseMove) { - handle_mouse_move_event(static_cast<const MouseEvent&>(event)); - return; - } - - if (event.type() == Event::MouseUp) { - open_hovered_item(); - return; - } - - if (event.type() == Event::MouseWheel && is_scrollable()) { - ASSERT(menu_window()); - auto& mouse_event = static_cast<const MouseEvent&>(event); - m_scroll_offset += mouse_event.wheel_delta(); - m_scroll_offset = clamp(m_scroll_offset, 0, m_max_scroll_offset); - - int index = item_index_at(mouse_event.position()); - if (m_hovered_item_index == index) - return; - - m_hovered_item_index = index; - update_for_new_hovered_item(); - return; - } - - if (event.type() == Event::KeyDown) { - auto key = static_cast<KeyEvent&>(event).key(); - - if (!(key == Key_Up || key == Key_Down || key == Key_Left || key == Key_Right || key == Key_Return)) - return; - - ASSERT(menu_window()); - ASSERT(menu_window()->is_visible()); - - // Default to the first item on key press if one has not been selected yet - if (!hovered_item()) { - m_hovered_item_index = 0; - update_for_new_hovered_item(key == Key_Right); - return; - } - - if (key == Key_Up) { - ASSERT(m_items.at(0).type() != MenuItem::Separator); - - if (is_scrollable() && m_hovered_item_index == 0) - return; - - auto original_index = m_hovered_item_index; - do { - if (m_hovered_item_index == 0) - m_hovered_item_index = m_items.size() - 1; - else - --m_hovered_item_index; - if (m_hovered_item_index == original_index) - return; - } while (hovered_item()->type() == MenuItem::Separator || !hovered_item()->is_enabled()); - - ASSERT(m_hovered_item_index >= 0 && m_hovered_item_index <= static_cast<int>(m_items.size()) - 1); - - if (is_scrollable() && m_hovered_item_index < m_scroll_offset) - --m_scroll_offset; - - update_for_new_hovered_item(); - return; - } - - if (key == Key_Down) { - ASSERT(m_items.at(0).type() != MenuItem::Separator); - - if (is_scrollable() && m_hovered_item_index == static_cast<int>(m_items.size()) - 1) - return; - - auto original_index = m_hovered_item_index; - do { - if (m_hovered_item_index == static_cast<int>(m_items.size()) - 1) - m_hovered_item_index = 0; - else - ++m_hovered_item_index; - if (m_hovered_item_index == original_index) - return; - } while (hovered_item()->type() == MenuItem::Separator || !hovered_item()->is_enabled()); - - ASSERT(m_hovered_item_index >= 0 && m_hovered_item_index <= static_cast<int>(m_items.size()) - 1); - - if (is_scrollable() && m_hovered_item_index >= (m_scroll_offset + visible_item_count())) - ++m_scroll_offset; - - update_for_new_hovered_item(); - return; - } - } - Core::Object::event(event); -} - -void Menu::clear_hovered_item() -{ - if (!hovered_item()) - return; - m_hovered_item_index = -1; - redraw(); -} - -void Menu::did_activate(MenuItem& item) -{ - if (item.type() == MenuItem::Type::Separator) - return; - - if (on_item_activation) - on_item_activation(item); - - MenuManager::the().close_bar(); - - if (m_client) - m_client->post_message(Messages::WindowClient::MenuItemActivated(m_menu_id, item.identifier())); -} - -bool Menu::activate_default() -{ - for (auto& item : m_items) { - if (item.type() == MenuItem::Type::Separator) - continue; - if (item.is_enabled() && item.is_default()) { - did_activate(item); - return true; - } - } - return false; -} - -MenuItem* Menu::item_with_identifier(unsigned identifier) -{ - for (auto& item : m_items) { - if (item.identifier() == identifier) - return &item; - } - return nullptr; -} - -int Menu::item_index_at(const Gfx::IntPoint& position) -{ - int i = 0; - for (auto& item : m_items) { - if (item.rect().contains(position)) - return i; - ++i; - } - return -1; -} - -void Menu::close() -{ - MenuManager::the().close_menu_and_descendants(*this); -} - -void Menu::redraw_if_theme_changed() -{ - if (m_theme_index_at_last_paint != MenuManager::the().theme_index()) - redraw(); -} - -void Menu::popup(const Gfx::IntPoint& position) -{ - do_popup(position, true); -} - -void Menu::do_popup(const Gfx::IntPoint& position, bool make_input) -{ - if (is_empty()) { - dbgln("Menu: Empty menu popup"); - return; - } - - auto& window = ensure_menu_window(); - redraw_if_theme_changed(); - - const int margin = 30; - Gfx::IntPoint adjusted_pos = position; - - if (adjusted_pos.x() + window.width() >= Screen::the().width() - margin) { - adjusted_pos = adjusted_pos.translated(-window.width(), 0); - } - if (adjusted_pos.y() + window.height() >= Screen::the().height() - margin) { - adjusted_pos = adjusted_pos.translated(0, -window.height()); - } - - if (adjusted_pos.y() < MenuManager::the().menubar_rect().height()) - adjusted_pos.set_y(MenuManager::the().menubar_rect().height()); - - window.move_to(adjusted_pos); - window.set_visible(true); - MenuManager::the().open_menu(*this, make_input); - WindowManager::the().did_popup_a_menu({}); -} - -bool Menu::is_menu_ancestor_of(const Menu& other) const -{ - for (auto& item : m_items) { - if (!item.is_submenu()) - continue; - auto& submenu = *item.submenu(); - if (&submenu == &other) - return true; - if (submenu.is_menu_ancestor_of(other)) - return true; - } - return false; -} - -} diff --git a/Services/WindowServer/Menu.h b/Services/WindowServer/Menu.h deleted file mode 100644 index 20fdb201cc..0000000000 --- a/Services/WindowServer/Menu.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/NonnullOwnPtrVector.h> -#include <AK/String.h> -#include <AK/WeakPtr.h> -#include <LibCore/Object.h> -#include <LibGfx/Font.h> -#include <LibGfx/FontDatabase.h> -#include <LibGfx/Forward.h> -#include <LibGfx/Rect.h> -#include <WindowServer/Cursor.h> -#include <WindowServer/MenuItem.h> -#include <WindowServer/Window.h> - -namespace WindowServer { - -class ClientConnection; -class MenuBar; -class Event; - -class Menu final : public Core::Object { - C_OBJECT(Menu) -public: - Menu(ClientConnection*, int menu_id, const String& name); - virtual ~Menu() override; - - ClientConnection* client() { return m_client; } - const ClientConnection* client() const { return m_client; } - int menu_id() const { return m_menu_id; } - - MenuBar* menubar() { return m_menubar; } - const MenuBar* menubar() const { return m_menubar; } - void set_menubar(MenuBar* menubar) { m_menubar = menubar; } - - bool is_empty() const { return m_items.is_empty(); } - int item_count() const { return m_items.size(); } - const MenuItem& item(int index) const { return m_items.at(index); } - MenuItem& item(int index) { return m_items.at(index); } - - void add_item(NonnullOwnPtr<MenuItem>&& item) { m_items.append(move(item)); } - - String name() const { return m_name; } - - template<typename Callback> - void for_each_item(Callback callback) const - { - for (auto& item : m_items) - callback(item); - } - - Gfx::IntRect text_rect_in_menubar() const { return m_text_rect_in_menubar; } - void set_text_rect_in_menubar(const Gfx::IntRect& rect) { m_text_rect_in_menubar = rect; } - - Gfx::IntRect rect_in_menubar() const { return m_rect_in_menubar; } - void set_rect_in_menubar(const Gfx::IntRect& rect) { m_rect_in_menubar = rect; } - - Window* menu_window() { return m_menu_window.ptr(); } - Window& ensure_menu_window(); - - Window* window_menu_of() { return m_window_menu_of; } - void set_window_menu_of(Window& window) { m_window_menu_of = window; } - bool is_window_menu_open() { return m_is_window_menu_open; } - void set_window_menu_open(bool is_open) { m_is_window_menu_open = is_open; } - - bool activate_default(); - - int content_width() const; - - int item_height() const { return 20; } - int frame_thickness() const { return 3; } - int horizontal_padding() const { return left_padding() + right_padding(); } - int left_padding() const { return 14; } - int right_padding() const { return 14; } - - void draw(); - const Gfx::Font& font() const; - const Gfx::Font& title_font() const; - void set_title_font(const Gfx::Font& font); - - MenuItem* item_with_identifier(unsigned); - void redraw(); - - MenuItem* hovered_item() const; - - void set_hovered_item(int index) - { - m_hovered_item_index = index; - update_for_new_hovered_item(); - } - - void clear_hovered_item(); - - Function<void(MenuItem&)> on_item_activation; - - void close(); - - void popup(const Gfx::IntPoint&); - void do_popup(const Gfx::IntPoint&, bool); - - bool is_menu_ancestor_of(const Menu&) const; - - void redraw_if_theme_changed(); - - bool is_scrollable() const { return m_scrollable; } - int scroll_offset() const { return m_scroll_offset; } - - void descend_into_submenu_at_hovered_item(); - void open_hovered_item(); - -private: - virtual void event(Core::Event&) override; - - RefPtr<Gfx::Font> m_title_font { &Gfx::FontDatabase::default_font() }; - - void handle_mouse_move_event(const MouseEvent&); - int visible_item_count() const; - - int item_index_at(const Gfx::IntPoint&); - int padding_between_text_and_shortcut() const { return 50; } - void did_activate(MenuItem&); - void update_for_new_hovered_item(bool make_input = false); - - ClientConnection* m_client { nullptr }; - int m_menu_id { 0 }; - String m_name; - Gfx::IntRect m_rect_in_menubar; - Gfx::IntRect m_text_rect_in_menubar; - MenuBar* m_menubar { nullptr }; - NonnullOwnPtrVector<MenuItem> m_items; - RefPtr<Window> m_menu_window; - - WeakPtr<Window> m_window_menu_of; - bool m_is_window_menu_open = { false }; - Gfx::IntPoint m_last_position_in_hover; - int m_theme_index_at_last_paint { -1 }; - int m_hovered_item_index { -1 }; - - bool m_scrollable { false }; - int m_scroll_offset { 0 }; - int m_max_scroll_offset { 0 }; -}; - -} diff --git a/Services/WindowServer/MenuBar.cpp b/Services/WindowServer/MenuBar.cpp deleted file mode 100644 index 30d739aaf7..0000000000 --- a/Services/WindowServer/MenuBar.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "MenuBar.h" -#include "Menu.h" -#include "MenuItem.h" -#include <LibGfx/Bitmap.h> - -namespace WindowServer { - -MenuBar::MenuBar(ClientConnection& client, int menubar_id) - : m_client(client) - , m_menubar_id(menubar_id) -{ -} - -MenuBar::~MenuBar() -{ -} - -void MenuBar::add_menu(Menu& menu) -{ - menu.set_menubar(this); - - // NOTE: We assume that the first menu is the App menu, which has a bold font. - if (m_menus.is_empty()) - menu.set_title_font(Gfx::FontDatabase::default_bold_font()); - - m_menus.append(&menu); -} - -} diff --git a/Services/WindowServer/MenuBar.h b/Services/WindowServer/MenuBar.h deleted file mode 100644 index 31dff9ac0f..0000000000 --- a/Services/WindowServer/MenuBar.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "Menu.h" -#include <AK/Vector.h> -#include <AK/WeakPtr.h> -#include <AK/Weakable.h> - -namespace WindowServer { - -class MenuBar : public Weakable<MenuBar> { -public: - MenuBar(ClientConnection& client, int menubar_id); - ~MenuBar(); - - ClientConnection& client() { return m_client; } - const ClientConnection& client() const { return m_client; } - int menubar_id() const { return m_menubar_id; } - void add_menu(Menu&); - - template<typename Callback> - void for_each_menu(Callback callback) - { - for (auto& menu : m_menus) { - if (callback(*menu) == IterationDecision::Break) - return; - } - } - -private: - ClientConnection& m_client; - int m_menubar_id { 0 }; - Vector<Menu*> m_menus; -}; - -} diff --git a/Services/WindowServer/MenuItem.cpp b/Services/WindowServer/MenuItem.cpp deleted file mode 100644 index 324724a893..0000000000 --- a/Services/WindowServer/MenuItem.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "MenuItem.h" -#include "ClientConnection.h" -#include "Menu.h" -#include "WindowManager.h" -#include <LibGfx/Bitmap.h> - -namespace WindowServer { - -MenuItem::MenuItem(Menu& menu, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked, const Gfx::Bitmap* icon) - : m_menu(menu) - , m_type(Text) - , m_enabled(enabled) - , m_checkable(checkable) - , m_checked(checked) - , m_identifier(identifier) - , m_text(text) - , m_shortcut_text(shortcut_text) - , m_icon(icon) -{ -} - -MenuItem::MenuItem(Menu& menu, Type type) - : m_menu(menu) - , m_type(type) -{ -} - -MenuItem::~MenuItem() -{ -} - -void MenuItem::set_enabled(bool enabled) -{ - if (m_enabled == enabled) - return; - m_enabled = enabled; - m_menu.redraw(); -} - -void MenuItem::set_checked(bool checked) -{ - if (m_checked == checked) - return; - m_checked = checked; - m_menu.redraw(); -} - -void MenuItem::set_default(bool is_default) -{ - if (m_default == is_default) - return; - m_default = is_default; - m_menu.redraw(); -} - -Menu* MenuItem::submenu() -{ - ASSERT(is_submenu()); - ASSERT(m_menu.client()); - return m_menu.client()->find_menu_by_id(m_submenu_id); -} - -const Menu* MenuItem::submenu() const -{ - ASSERT(is_submenu()); - ASSERT(m_menu.client()); - return m_menu.client()->find_menu_by_id(m_submenu_id); -} - -Gfx::IntRect MenuItem::rect() const -{ - if (!m_menu.is_scrollable()) - return m_rect; - return m_rect.translated(0, m_menu.item_height() - (m_menu.scroll_offset() * m_menu.item_height())); -} - -void MenuItem::set_icon(const Gfx::Bitmap* icon) -{ - if (m_icon == icon) - return; - m_icon = icon; - m_menu.redraw(); -} - -} diff --git a/Services/WindowServer/MenuItem.h b/Services/WindowServer/MenuItem.h deleted file mode 100644 index 6bb1c203f1..0000000000 --- a/Services/WindowServer/MenuItem.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Function.h> -#include <AK/String.h> -#include <LibGfx/Forward.h> -#include <LibGfx/Rect.h> - -namespace WindowServer { - -class Menu; - -class MenuItem { -public: - enum Type { - None, - Text, - Separator, - }; - - MenuItem(Menu&, unsigned identifier, const String& text, const String& shortcut_text = {}, bool enabled = true, bool checkable = false, bool checked = false, const Gfx::Bitmap* icon = nullptr); - MenuItem(Menu&, Type); - ~MenuItem(); - - Type type() const { return m_type; } - - bool is_enabled() const { return m_enabled; } - void set_enabled(bool); - - bool is_checkable() const { return m_checkable; } - void set_checkable(bool checkable) { m_checkable = checkable; } - - bool is_checked() const { return m_checked; } - void set_checked(bool); - - bool is_default() const { return m_default; } - void set_default(bool); - - String text() const { return m_text; } - void set_text(const String& text) { m_text = text; } - - String shortcut_text() const { return m_shortcut_text; } - void set_shortcut_text(const String& text) { m_shortcut_text = text; } - - void set_rect(const Gfx::IntRect& rect) { m_rect = rect; } - Gfx::IntRect rect() const; - - unsigned identifier() const { return m_identifier; } - - const Gfx::Bitmap* icon() const { return m_icon; } - void set_icon(const Gfx::Bitmap*); - - bool is_submenu() const { return m_submenu_id != -1; } - int submenu_id() const { return m_submenu_id; } - void set_submenu_id(int submenu_id) { m_submenu_id = submenu_id; } - - Menu* submenu(); - const Menu* submenu() const; - - bool is_exclusive() const { return m_exclusive; } - void set_exclusive(bool exclusive) { m_exclusive = exclusive; } - -private: - Menu& m_menu; - Type m_type { None }; - bool m_enabled { true }; - bool m_checkable { false }; - bool m_checked { false }; - bool m_default { false }; - unsigned m_identifier { 0 }; - String m_text; - String m_shortcut_text; - Gfx::IntRect m_rect; - RefPtr<Gfx::Bitmap> m_icon; - int m_submenu_id { -1 }; - bool m_exclusive { false }; -}; - -} diff --git a/Services/WindowServer/MenuManager.cpp b/Services/WindowServer/MenuManager.cpp deleted file mode 100644 index ba2ba789f9..0000000000 --- a/Services/WindowServer/MenuManager.cpp +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * Copyright (c) 2020, Shannon Booth <shannon.ml.booth@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Badge.h> -#include <AK/QuickSort.h> -#include <LibCore/DirIterator.h> -#include <LibGfx/Font.h> -#include <LibGfx/Painter.h> -#include <WindowServer/AppletManager.h> -#include <WindowServer/MenuManager.h> -#include <WindowServer/Screen.h> -#include <WindowServer/WindowManager.h> -#include <unistd.h> - -//#define DEBUG_MENUS - -namespace WindowServer { - -static MenuManager* s_the; -static constexpr int s_search_timeout = 3000; - -MenuManager& MenuManager::the() -{ - ASSERT(s_the); - return *s_the; -} - -MenuManager::MenuManager() -{ - s_the = this; - m_needs_window_resize = true; - - // NOTE: This ensures that the system menu has the correct dimensions. - set_current_menubar(nullptr); - - m_window = Window::construct(*this, WindowType::Menubar); - m_window->set_rect(menubar_rect()); - - m_search_timer = Core::Timer::create_single_shot(0, [this] { - m_current_search.clear(); - }); -} - -MenuManager::~MenuManager() -{ -} - -bool MenuManager::is_open(const Menu& menu) const -{ - for (size_t i = 0; i < m_open_menu_stack.size(); ++i) { - if (&menu == m_open_menu_stack[i].ptr()) - return true; - } - return false; -} - -void MenuManager::draw() -{ - auto& wm = WindowManager::the(); - auto palette = wm.palette(); - auto menubar_rect = this->menubar_rect(); - - if (m_needs_window_resize) { - m_window->set_rect(menubar_rect); - AppletManager::the().calculate_applet_rects(window()); - m_needs_window_resize = false; - } - - Gfx::Painter painter(*window().backing_store()); - - painter.fill_rect(menubar_rect, palette.window()); - painter.draw_line({ 0, menubar_rect.bottom() - 1 }, { menubar_rect.right(), menubar_rect.bottom() - 1 }, palette.threed_shadow1()); - painter.draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, palette.threed_shadow2()); - - for_each_active_menubar_menu([&](Menu& menu) { - Color text_color = palette.window_text(); - if (is_open(menu)) { - painter.fill_rect(menu.rect_in_menubar(), palette.menu_selection()); - painter.draw_rect(menu.rect_in_menubar(), palette.menu_selection().darkened()); - text_color = palette.menu_selection_text(); - } - painter.draw_text( - menu.text_rect_in_menubar(), - menu.name(), - menu.title_font(), - Gfx::TextAlignment::CenterLeft, - text_color); - //painter.draw_rect(menu.text_rect_in_menubar(), Color::Magenta); - return IterationDecision::Continue; - }); - - AppletManager::the().draw(); -} - -void MenuManager::refresh() -{ - if (!m_window) - return; - draw(); - window().invalidate(); -} - -void MenuManager::event(Core::Event& event) -{ - if (static_cast<Event&>(event).is_mouse_event()) { - handle_mouse_event(static_cast<MouseEvent&>(event)); - return; - } - - if (static_cast<Event&>(event).is_key_event()) { - auto& key_event = static_cast<const KeyEvent&>(event); - - if (key_event.type() == Event::KeyUp && key_event.key() == Key_Escape) { - close_everyone(); - return; - } - - if (key_event.key() == Key_Backspace) { - m_current_search.clear(); - return; - } - - if (m_current_menu && event.type() == Event::KeyDown - && ((key_event.key() >= Key_A && key_event.key() <= Key_Z) - || (key_event.key() >= Key_0 && key_event.key() <= Key_9))) { - m_current_search.append_code_point(key_event.code_point()); - m_search_timer->restart(s_search_timeout); - for (int i = 0; i < m_current_menu->item_count(); ++i) { - auto text = m_current_menu->item(i).text(); - if (text.to_lowercase().starts_with(m_current_search.to_string().to_lowercase())) { - m_current_menu->set_hovered_item(i); - return; - } - } - return; - } - - if (event.type() == Event::KeyDown) { - - if (key_event.key() == Key_Left) { - auto it = m_open_menu_stack.find_if([&](const auto& other) { return m_current_menu == other.ptr(); }); - ASSERT(!it.is_end()); - - // Going "back" a menu should be the previous menu in the stack - if (it.index() > 0) - set_current_menu(m_open_menu_stack.at(it.index() - 1)); - close_everyone_not_in_lineage(*m_current_menu); - return; - } - - if (key_event.key() == Key_Right) { - auto hovered_item = m_current_menu->hovered_item(); - if (hovered_item && hovered_item->is_submenu()) - m_current_menu->descend_into_submenu_at_hovered_item(); - return; - } - - if (key_event.key() == Key_Return) { - auto hovered_item = m_current_menu->hovered_item(); - if (!hovered_item || !hovered_item->is_enabled()) - return; - if (hovered_item->is_submenu()) - m_current_menu->descend_into_submenu_at_hovered_item(); - else - m_current_menu->open_hovered_item(); - return; - } - m_current_menu->dispatch_event(event); - } - } - - return Core::Object::event(event); -} - -void MenuManager::handle_mouse_event(MouseEvent& mouse_event) -{ - auto* active_window = WindowManager::the().active_window(); - bool handled_menubar_event = false; - for_each_active_menubar_menu([&](Menu& menu) { - if (menu.rect_in_menubar().contains(mouse_event.position())) { - handled_menubar_event = &menu == m_system_menu || !active_window || !active_window->is_modal(); - if (handled_menubar_event) - handle_menu_mouse_event(menu, mouse_event); - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - if (handled_menubar_event) - return; - - if (has_open_menu()) { - auto* topmost_menu = m_open_menu_stack.last().ptr(); - ASSERT(topmost_menu); - auto* window = topmost_menu->menu_window(); - if (!window) { - dbgln("MenuManager::handle_mouse_event: No menu window"); - return; - } - ASSERT(window->is_visible()); - - bool event_is_inside_current_menu = window->rect().contains(mouse_event.position()); - if (event_is_inside_current_menu) { - WindowManager::the().set_hovered_window(window); - auto translated_event = mouse_event.translated(-window->position()); - WindowManager::the().deliver_mouse_event(*window, translated_event); - return; - } - - if (topmost_menu->hovered_item()) - topmost_menu->clear_hovered_item(); - if (mouse_event.type() == Event::MouseDown || mouse_event.type() == Event::MouseUp) { - auto* window_menu_of = topmost_menu->window_menu_of(); - if (window_menu_of) { - bool event_is_inside_taskbar_button = window_menu_of->taskbar_rect().contains(mouse_event.position()); - if (event_is_inside_taskbar_button && !topmost_menu->is_window_menu_open()) { - topmost_menu->set_window_menu_open(true); - return; - } - } - - if (mouse_event.type() == Event::MouseDown) { - close_bar(); - topmost_menu->set_window_menu_open(false); - } - } - - if (mouse_event.type() == Event::MouseMove) { - for (auto& menu : m_open_menu_stack) { - if (!menu) - continue; - if (!menu->menu_window()->rect().contains(mouse_event.position())) - continue; - WindowManager::the().set_hovered_window(menu->menu_window()); - auto translated_event = mouse_event.translated(-menu->menu_window()->position()); - WindowManager::the().deliver_mouse_event(*menu->menu_window(), translated_event); - break; - } - } - return; - } - - AppletManager::the().dispatch_event(static_cast<Event&>(mouse_event)); -} - -void MenuManager::handle_menu_mouse_event(Menu& menu, const MouseEvent& event) -{ - bool is_hover_with_any_menu_open = event.type() == MouseEvent::MouseMove - && has_open_menu() - && (m_open_menu_stack.first()->menubar() || m_open_menu_stack.first() == m_system_menu.ptr()); - bool is_mousedown_with_left_button = event.type() == MouseEvent::MouseDown && event.button() == MouseButton::Left; - bool should_open_menu = &menu != m_current_menu && (is_hover_with_any_menu_open || is_mousedown_with_left_button); - - if (is_mousedown_with_left_button) - m_bar_open = !m_bar_open; - - if (should_open_menu && m_bar_open) { - close_everyone(); - open_menu(menu); - return; - } - - if (!m_bar_open) - close_everyone(); -} - -void MenuManager::set_needs_window_resize() -{ - m_needs_window_resize = true; -} - -void MenuManager::close_all_menus_from_client(Badge<ClientConnection>, ClientConnection& client) -{ - if (!has_open_menu()) - return; - if (m_open_menu_stack.first()->client() != &client) - return; - close_everyone(); -} - -void MenuManager::close_everyone() -{ - for (auto& menu : m_open_menu_stack) { - ASSERT(menu); - if (menu->menu_window()) - menu->menu_window()->set_visible(false); - menu->clear_hovered_item(); - } - m_open_menu_stack.clear(); - m_current_search.clear(); - clear_current_menu(); - refresh(); -} - -void MenuManager::close_everyone_not_in_lineage(Menu& menu) -{ - Vector<Menu*> menus_to_close; - for (auto& open_menu : m_open_menu_stack) { - if (!open_menu) - continue; - if (&menu == open_menu.ptr() || open_menu->is_menu_ancestor_of(menu)) - continue; - menus_to_close.append(open_menu); - } - close_menus(menus_to_close); -} - -void MenuManager::close_menus(const Vector<Menu*>& menus) -{ - for (auto& menu : menus) { - if (menu == m_current_menu) - clear_current_menu(); - if (menu->menu_window()) - menu->menu_window()->set_visible(false); - menu->clear_hovered_item(); - m_open_menu_stack.remove_first_matching([&](auto& entry) { - return entry == menu; - }); - } - refresh(); -} - -static void collect_menu_subtree(Menu& menu, Vector<Menu*>& menus) -{ - menus.append(&menu); - for (int i = 0; i < menu.item_count(); ++i) { - auto& item = menu.item(i); - if (!item.is_submenu()) - continue; - collect_menu_subtree(*item.submenu(), menus); - } -} - -void MenuManager::close_menu_and_descendants(Menu& menu) -{ - Vector<Menu*> menus_to_close; - collect_menu_subtree(menu, menus_to_close); - close_menus(menus_to_close); -} - -void MenuManager::toggle_menu(Menu& menu) -{ - if (is_open(menu)) { - close_menu_and_descendants(menu); - return; - } - open_menu(menu); -} - -void MenuManager::open_menu(Menu& menu, bool as_current_menu) -{ - if (is_open(menu)) { - if (as_current_menu || current_menu() != &menu) { - // This menu is already open. If requested, or if the current - // window doesn't match this one, then set it to this - set_current_menu(&menu); - } - return; - } - - if (!menu.is_empty()) { - menu.redraw_if_theme_changed(); - if (!menu.menu_window()) { - auto& menu_window = menu.ensure_menu_window(); - menu_window.move_to({ menu.rect_in_menubar().x(), menu.rect_in_menubar().bottom() + 2 }); - } - menu.menu_window()->set_visible(true); - } - - if (m_open_menu_stack.find_if([&menu](auto& other) { return &menu == other.ptr(); }).is_end()) - m_open_menu_stack.append(menu); - - if (as_current_menu || !current_menu()) { - // Only make this menu the current menu if requested, or if no - // other menu is current - set_current_menu(&menu); - } - - refresh(); -} - -void MenuManager::clear_current_menu() -{ - Menu* previous_current_menu = m_current_menu; - m_current_menu = nullptr; - if (previous_current_menu) { - // When closing the last menu, restore the previous active input window - auto& wm = WindowManager::the(); - wm.restore_active_input_window(m_previous_input_window); - } -} - -void MenuManager::set_current_menu(Menu* menu) -{ - if (!menu) { - clear_current_menu(); - return; - } - - ASSERT(is_open(*menu)); - if (menu == m_current_menu) { - return; - } - - m_current_search.clear(); - - Menu* previous_current_menu = m_current_menu; - m_current_menu = menu; - - auto& wm = WindowManager::the(); - if (!previous_current_menu) { - // When opening the first menu, store the current active input window - if (auto* active_input = wm.active_input_window()) - m_previous_input_window = *active_input; - else - m_previous_input_window = nullptr; - } - - wm.set_active_input_window(m_current_menu->menu_window()); -} - -void MenuManager::close_bar() -{ - close_everyone(); - m_bar_open = false; -} - -Gfx::IntRect MenuManager::menubar_rect() const -{ - return { 0, 0, Screen::the().rect().width(), 19 }; -} - -void MenuManager::set_current_menubar(MenuBar* menubar) -{ - if (menubar) - m_current_menubar = *menubar; - else - m_current_menubar = nullptr; -#ifdef DEBUG_MENUS - dbg() << "[WM] Current menubar is now " << menubar; -#endif - Gfx::IntPoint next_menu_location { MenuManager::menubar_menu_margin() / 2, 0 }; - for_each_active_menubar_menu([&](Menu& menu) { - int text_width = menu.title_font().width(menu.name()); - menu.set_rect_in_menubar({ next_menu_location.x() - MenuManager::menubar_menu_margin() / 2, 0, text_width + MenuManager::menubar_menu_margin(), menubar_rect().height() - 1 }); - - Gfx::IntRect text_rect { next_menu_location.translated(0, 1), { text_width, menubar_rect().height() - 3 } }; - - menu.set_text_rect_in_menubar(text_rect); - next_menu_location.move_by(menu.rect_in_menubar().width(), 0); - return IterationDecision::Continue; - }); - refresh(); -} - -void MenuManager::close_menubar(MenuBar& menubar) -{ - if (current_menubar() == &menubar) - set_current_menubar(nullptr); -} - -void MenuManager::set_system_menu(Menu& menu) -{ - m_system_menu = menu; - set_current_menubar(m_current_menubar); -} - -void MenuManager::did_change_theme() -{ - ++m_theme_index; - refresh(); -} - -} diff --git a/Services/WindowServer/MenuManager.h b/Services/WindowServer/MenuManager.h deleted file mode 100644 index 0d56f6a8b7..0000000000 --- a/Services/WindowServer/MenuManager.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include "Menu.h" -#include "MenuBar.h" -#include "Window.h" -#include <AK/HashMap.h> -#include <AK/StringBuilder.h> -#include <LibCore/Object.h> -#include <LibCore/Timer.h> - -namespace WindowServer { - -class MenuManager final : public Core::Object { - C_OBJECT(MenuManager) -public: - static MenuManager& the(); - - MenuManager(); - virtual ~MenuManager() override; - - void refresh(); - - bool is_open(const Menu&) const; - bool has_open_menu() const { return !m_open_menu_stack.is_empty(); } - - Gfx::IntRect menubar_rect() const; - static int menubar_menu_margin() { return 16; } - - void set_needs_window_resize(); - - Menu* current_menu() { return m_current_menu.ptr(); } - void set_current_menu(Menu*); - void clear_current_menu(); - void open_menu(Menu&, bool as_current_menu = true); - void toggle_menu(Menu&); - - MenuBar* current_menubar() { return m_current_menubar.ptr(); } - void set_current_menubar(MenuBar*); - void close_menubar(MenuBar&); - - void close_bar(); - void close_everyone(); - void close_everyone_not_in_lineage(Menu&); - void close_menu_and_descendants(Menu&); - - void close_all_menus_from_client(Badge<ClientConnection>, ClientConnection&); - - Menu* system_menu() { return m_system_menu; } - void set_system_menu(Menu&); - - int theme_index() const { return m_theme_index; } - - Window& window() { return *m_window; } - - template<typename Callback> - void for_each_active_menubar_menu(Callback callback) - { - if (system_menu()) { - if (callback(*system_menu()) == IterationDecision::Break) - return; - } - if (m_current_menubar) - m_current_menubar->for_each_menu(callback); - } - - void did_change_theme(); - -private: - void close_menus(const Vector<Menu*>&); - - const Window& window() const { return *m_window; } - - virtual void event(Core::Event&) override; - void handle_mouse_event(MouseEvent&); - void handle_menu_mouse_event(Menu&, const MouseEvent&); - - void draw(); - - RefPtr<Window> m_window; - - WeakPtr<Menu> m_current_menu; - WeakPtr<Window> m_previous_input_window; - Vector<WeakPtr<Menu>> m_open_menu_stack; - - RefPtr<Core::Timer> m_search_timer; - StringBuilder m_current_search; - WeakPtr<Menu> m_system_menu; - - bool m_needs_window_resize { false }; - bool m_bar_open { false }; - - int m_theme_index { 0 }; - - WeakPtr<MenuBar> m_current_menubar; -}; - -} diff --git a/Services/WindowServer/Screen.cpp b/Services/WindowServer/Screen.cpp deleted file mode 100644 index 4cc9ae809b..0000000000 --- a/Services/WindowServer/Screen.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Screen.h" -#include "Compositor.h" -#include "Event.h" -#include "EventLoop.h" -#include "WindowManager.h" -#include <Kernel/API/FB.h> -#include <Kernel/API/MousePacket.h> -#include <fcntl.h> -#include <stdio.h> -#include <sys/mman.h> -#include <unistd.h> - -namespace WindowServer { - -static Screen* s_the; - -Screen& Screen::the() -{ - ASSERT(s_the); - return *s_the; -} - -Screen::Screen(unsigned desired_width, unsigned desired_height) -{ - ASSERT(!s_the); - s_the = this; - m_framebuffer_fd = open("/dev/fb0", O_RDWR | O_CLOEXEC); - if (m_framebuffer_fd < 0) { - perror("failed to open /dev/fb0"); - ASSERT_NOT_REACHED(); - } - - if (fb_set_buffer(m_framebuffer_fd, 0) == 0) { - m_can_set_buffer = true; - } - - set_resolution(desired_width, desired_height); - m_cursor_location = rect().center(); -} - -Screen::~Screen() -{ - close(m_framebuffer_fd); -} - -bool Screen::set_resolution(int width, int height) -{ - FBResolution resolution { 0, (unsigned)width, (unsigned)height }; - int rc = fb_set_resolution(m_framebuffer_fd, &resolution); -#ifdef WSSCREEN_DEBUG - dbg() << "fb_set_resolution() - return code " << rc; -#endif - if (rc == 0) { - on_change_resolution(resolution.pitch, resolution.width, resolution.height); - return true; - } - if (rc == -1) { - dbg() << "Invalid resolution " << width << "x" << height; - on_change_resolution(resolution.pitch, resolution.width, resolution.height); - return false; - } - ASSERT_NOT_REACHED(); -} - -void Screen::on_change_resolution(int pitch, int width, int height) -{ - if (m_framebuffer) { - size_t previous_size_in_bytes = m_size_in_bytes; - int rc = munmap(m_framebuffer, previous_size_in_bytes); - ASSERT(rc == 0); - } - - int rc = fb_get_size_in_bytes(m_framebuffer_fd, &m_size_in_bytes); - ASSERT(rc == 0); - - m_framebuffer = (Gfx::RGBA32*)mmap(nullptr, m_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, m_framebuffer_fd, 0); - ASSERT(m_framebuffer && m_framebuffer != (void*)-1); - - m_pitch = pitch; - m_width = width; - m_height = height; - - m_cursor_location.constrain(rect()); -} - -void Screen::set_buffer(int index) -{ - ASSERT(m_can_set_buffer); - int rc = fb_set_buffer(m_framebuffer_fd, index); - ASSERT(rc == 0); -} - -void Screen::set_acceleration_factor(double factor) -{ - ASSERT(factor >= mouse_accel_min && factor <= mouse_accel_max); - m_acceleration_factor = factor; -} - -void Screen::set_scroll_step_size(unsigned step_size) -{ - ASSERT(step_size >= scroll_step_size_min); - m_scroll_step_size = step_size; -} - -void Screen::on_receive_mouse_data(const MousePacket& packet) -{ - auto prev_location = m_cursor_location; - if (packet.is_relative) { - m_cursor_location.move_by(packet.x * m_acceleration_factor, packet.y * m_acceleration_factor); -#ifdef WSSCREEN_DEBUG - dbgln("Screen: New Relative mouse point @ {}", m_cursor_location); -#endif - } else { - m_cursor_location = { packet.x * m_width / 0xffff, packet.y * m_height / 0xffff }; -#ifdef WSSCREEN_DEBUG - dbgln("Screen: New Absolute mouse point @ {}", m_cursor_location); -#endif - } - - m_cursor_location.constrain(rect()); - - unsigned buttons = packet.buttons; - unsigned prev_buttons = m_mouse_button_state; - m_mouse_button_state = buttons; - unsigned changed_buttons = prev_buttons ^ buttons; - auto post_mousedown_or_mouseup_if_needed = [&](MouseButton button) { - if (!(changed_buttons & (unsigned)button)) - return; - auto message = make<MouseEvent>(buttons & (unsigned)button ? Event::MouseDown : Event::MouseUp, m_cursor_location, buttons, button, m_modifiers); - Core::EventLoop::current().post_event(WindowManager::the(), move(message)); - }; - post_mousedown_or_mouseup_if_needed(MouseButton::Left); - post_mousedown_or_mouseup_if_needed(MouseButton::Right); - post_mousedown_or_mouseup_if_needed(MouseButton::Middle); - post_mousedown_or_mouseup_if_needed(MouseButton::Back); - post_mousedown_or_mouseup_if_needed(MouseButton::Forward); - if (m_cursor_location != prev_location) { - auto message = make<MouseEvent>(Event::MouseMove, m_cursor_location, buttons, MouseButton::None, m_modifiers); - if (WindowManager::the().dnd_client()) - message->set_mime_data(WindowManager::the().dnd_mime_data()); - Core::EventLoop::current().post_event(WindowManager::the(), move(message)); - } - - if (packet.z) { - auto message = make<MouseEvent>(Event::MouseWheel, m_cursor_location, buttons, MouseButton::None, m_modifiers, packet.z * m_scroll_step_size); - Core::EventLoop::current().post_event(WindowManager::the(), move(message)); - } - - if (m_cursor_location != prev_location) - Compositor::the().invalidate_cursor(); -} - -void Screen::on_receive_keyboard_data(::KeyEvent kernel_event) -{ - m_modifiers = kernel_event.modifiers(); - auto message = make<KeyEvent>(kernel_event.is_press() ? Event::KeyDown : Event::KeyUp, kernel_event.key, kernel_event.code_point, kernel_event.modifiers(), kernel_event.scancode); - Core::EventLoop::current().post_event(WindowManager::the(), move(message)); -} - -} diff --git a/Services/WindowServer/Screen.h b/Services/WindowServer/Screen.h deleted file mode 100644 index c435bc62c9..0000000000 --- a/Services/WindowServer/Screen.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <Kernel/API/KeyCode.h> -#include <LibGfx/Color.h> -#include <LibGfx/Rect.h> -#include <LibGfx/Size.h> - -struct MousePacket; - -namespace WindowServer { - -const double mouse_accel_max = 3.5; -const double mouse_accel_min = 0.5; -const unsigned scroll_step_size_min = 1; - -class Screen { -public: - Screen(unsigned width, unsigned height); - ~Screen(); - - bool set_resolution(int width, int height); - bool can_set_buffer() { return m_can_set_buffer; } - void set_buffer(int index); - - int width() const { return m_width; } - int height() const { return m_height; } - size_t pitch() const { return m_pitch; } - Gfx::RGBA32* scanline(int y); - - static Screen& the(); - - Gfx::IntSize size() const { return { width(), height() }; } - Gfx::IntRect rect() const { return { 0, 0, width(), height() }; } - - Gfx::IntPoint cursor_location() const { return m_cursor_location; } - unsigned mouse_button_state() const { return m_mouse_button_state; } - - double acceleration_factor() const { return m_acceleration_factor; } - void set_acceleration_factor(double); - - unsigned scroll_step_size() const { return m_scroll_step_size; } - void set_scroll_step_size(unsigned); - - void on_receive_mouse_data(const MousePacket&); - void on_receive_keyboard_data(::KeyEvent); - -private: - void on_change_resolution(int pitch, int width, int height); - - size_t m_size_in_bytes; - - Gfx::RGBA32* m_framebuffer { nullptr }; - bool m_can_set_buffer { false }; - - int m_pitch { 0 }; - int m_width { 0 }; - int m_height { 0 }; - int m_framebuffer_fd { -1 }; - - Gfx::IntPoint m_cursor_location; - unsigned m_mouse_button_state { 0 }; - unsigned m_modifiers { 0 }; - double m_acceleration_factor { 1.0 }; - unsigned m_scroll_step_size { 1 }; -}; - -inline Gfx::RGBA32* Screen::scanline(int y) -{ - return reinterpret_cast<Gfx::RGBA32*>(((u8*)m_framebuffer) + (y * m_pitch)); -} - -} diff --git a/Services/WindowServer/Window.cpp b/Services/WindowServer/Window.cpp deleted file mode 100644 index 1d0497c1c6..0000000000 --- a/Services/WindowServer/Window.cpp +++ /dev/null @@ -1,776 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "Window.h" -#include "AppletManager.h" -#include "ClientConnection.h" -#include "Compositor.h" -#include "Event.h" -#include "EventLoop.h" -#include "Screen.h" -#include "WindowManager.h" -#include <AK/Badge.h> -#include <WindowServer/WindowClientEndpoint.h> - -namespace WindowServer { - -static String default_window_icon_path() -{ - return "/res/icons/16x16/window.png"; -} - -static Gfx::Bitmap& default_window_icon() -{ - static Gfx::Bitmap* s_icon; - if (!s_icon) - s_icon = Gfx::Bitmap::load_from_file(default_window_icon_path()).leak_ref(); - return *s_icon; -} - -static Gfx::Bitmap& minimize_icon() -{ - static Gfx::Bitmap* s_icon; - if (!s_icon) - s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png").leak_ref(); - return *s_icon; -} - -static Gfx::Bitmap& maximize_icon() -{ - static Gfx::Bitmap* s_icon; - if (!s_icon) - s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png").leak_ref(); - return *s_icon; -} - -static Gfx::Bitmap& restore_icon() -{ - static Gfx::Bitmap* s_icon; - if (!s_icon) - s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-restore.png").leak_ref(); - return *s_icon; -} - -static Gfx::Bitmap& close_icon() -{ - static Gfx::Bitmap* s_icon; - if (!s_icon) - s_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close.png").leak_ref(); - return *s_icon; -} - -Window::Window(Core::Object& parent, WindowType type) - : Core::Object(&parent) - , m_type(type) - , m_icon(default_window_icon()) - , m_frame(*this) -{ - WindowManager::the().add_window(*this); -} - -Window::Window(ClientConnection& client, WindowType window_type, int window_id, bool modal, bool minimizable, bool frameless, bool resizable, bool fullscreen, bool accessory, Window* parent_window) - : Core::Object(&client) - , m_client(&client) - , m_type(window_type) - , m_modal(modal) - , m_minimizable(minimizable) - , m_frameless(frameless) - , m_resizable(resizable) - , m_fullscreen(fullscreen) - , m_accessory(accessory) - , m_window_id(window_id) - , m_client_id(client.client_id()) - , m_icon(default_window_icon()) - , m_frame(*this) -{ - // FIXME: This should not be hard-coded here. - if (m_type == WindowType::Taskbar) { - m_wm_event_mask = WMEventMask::WindowStateChanges | WMEventMask::WindowRemovals | WMEventMask::WindowIconChanges; - m_listens_to_wm_events = true; - } - - if (parent_window) - set_parent_window(*parent_window); - WindowManager::the().add_window(*this); -} - -Window::~Window() -{ - // Detach from client at the start of teardown since we don't want - // to confuse things by trying to send messages to it. - m_client = nullptr; - - WindowManager::the().remove_window(*this); -} - -void Window::destroy() -{ - m_destroyed = true; - set_visible(false); -} - -void Window::set_title(const String& title) -{ - if (m_title == title) - return; - m_title = title; - frame().invalidate_title_bar(); - WindowManager::the().notify_title_changed(*this); -} - -void Window::set_rect(const Gfx::IntRect& rect) -{ - ASSERT(!rect.is_empty()); - if (m_rect == rect) - return; - auto old_rect = m_rect; - m_rect = rect; - if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) { - m_backing_store = Gfx::Bitmap::create(Gfx::BitmapFormat::RGB32, m_rect.size()); - } - - invalidate(true); - m_frame.notify_window_rect_changed(old_rect, rect); // recomputes occlusions -} - -void Window::set_rect_without_repaint(const Gfx::IntRect& rect) -{ - ASSERT(!rect.is_empty()); - if (m_rect == rect) - return; - auto old_rect = m_rect; - m_rect = rect; - - if (old_rect.size() == m_rect.size()) { - auto delta = m_rect.location() - old_rect.location(); - for (auto& child_window : m_child_windows) { - if (child_window) - child_window->move_by(delta); - } - } - - invalidate(true); - m_frame.notify_window_rect_changed(old_rect, rect); // recomputes occlusions -} - -void Window::handle_mouse_event(const MouseEvent& event) -{ - set_automatic_cursor_tracking_enabled(event.buttons() != 0); - - switch (event.type()) { - case Event::MouseMove: - m_client->post_message(Messages::WindowClient::MouseMove(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta(), event.is_drag(), event.mime_types())); - break; - case Event::MouseDown: - m_client->post_message(Messages::WindowClient::MouseDown(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta())); - break; - case Event::MouseDoubleClick: - m_client->post_message(Messages::WindowClient::MouseDoubleClick(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta())); - break; - case Event::MouseUp: - m_client->post_message(Messages::WindowClient::MouseUp(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta())); - break; - case Event::MouseWheel: - m_client->post_message(Messages::WindowClient::MouseWheel(m_window_id, event.position(), (u32)event.button(), event.buttons(), event.modifiers(), event.wheel_delta())); - break; - default: - ASSERT_NOT_REACHED(); - } -} - -void Window::update_menu_item_text(PopupMenuItem item) -{ - if (m_window_menu) { - m_window_menu->item((int)item).set_text(item == PopupMenuItem::Minimize ? (m_minimized ? "Unminimize" : "Minimize") : (m_maximized ? "Restore" : "Maximize")); - m_window_menu->redraw(); - } -} - -void Window::update_menu_item_enabled(PopupMenuItem item) -{ - if (m_window_menu) { - m_window_menu->item((int)item).set_enabled(item == PopupMenuItem::Minimize ? m_minimizable : m_resizable); - m_window_menu->redraw(); - } -} - -void Window::set_minimized(bool minimized) -{ - if (m_minimized == minimized) - return; - if (minimized && !m_minimizable) - return; - m_minimized = minimized; - update_menu_item_text(PopupMenuItem::Minimize); - Compositor::the().invalidate_occlusions(); - Compositor::the().invalidate_screen(frame().rect()); - if (!blocking_modal_window()) - start_minimize_animation(); - if (!minimized) - request_update({ {}, size() }); - WindowManager::the().notify_minimization_state_changed(*this); -} - -void Window::set_minimizable(bool minimizable) -{ - if (m_minimizable == minimizable) - return; - m_minimizable = minimizable; - update_menu_item_enabled(PopupMenuItem::Minimize); - // TODO: Hide/show (or alternatively change enabled state of) window minimize button dynamically depending on value of m_minimizable -} - -void Window::set_taskbar_rect(const Gfx::IntRect& rect) -{ - m_taskbar_rect = rect; - m_have_taskbar_rect = !m_taskbar_rect.is_empty(); -} - -void Window::start_minimize_animation() -{ - if (!m_have_taskbar_rect) { - // If this is a modal window, it may not have its own taskbar - // button, so there is no rectangle. In that case, walk the - // modal stack until we find a window that may have one - WindowManager::the().for_each_window_in_modal_stack(*this, [&](Window& w, bool) { - if (w.has_taskbar_rect()) { - // We purposely do NOT set m_have_taskbar_rect to true here - // because we want to only copy the rectangle from the - // window that has it, but since this window wouldn't receive - // any updates down the road we want to query it again - // next time we want to start the animation - m_taskbar_rect = w.taskbar_rect(); - - ASSERT(!m_have_taskbar_rect); // should remain unset! - return IterationDecision::Break; - }; - return IterationDecision::Continue; - }); - } - m_minimize_animation_step = 0; -} - -void Window::set_opacity(float opacity) -{ - if (m_opacity == opacity) - return; - bool was_opaque = is_opaque(); - m_opacity = opacity; - if (was_opaque != is_opaque()) - Compositor::the().invalidate_occlusions(); - Compositor::the().invalidate_screen(frame().rect()); - WindowManager::the().notify_opacity_changed(*this); -} - -void Window::set_occluded(bool occluded) -{ - if (m_occluded == occluded) - return; - m_occluded = occluded; - WindowManager::the().notify_occlusion_state_changed(*this); -} - -void Window::set_maximized(bool maximized) -{ - if (m_maximized == maximized) - return; - if (maximized && (!is_resizable() || resize_aspect_ratio().has_value())) - return; - set_tiled(WindowTileType::None); - m_maximized = maximized; - update_menu_item_text(PopupMenuItem::Maximize); - if (maximized) { - m_unmaximized_rect = m_rect; - set_rect(WindowManager::the().maximized_window_rect(*this)); - } else { - set_rect(m_unmaximized_rect); - } - m_frame.did_set_maximized({}, maximized); - Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect)); - set_default_positioned(false); -} - -void Window::set_resizable(bool resizable) -{ - if (m_resizable == resizable) - return; - m_resizable = resizable; - update_menu_item_enabled(PopupMenuItem::Maximize); - // TODO: Hide/show (or alternatively change enabled state of) window maximize button dynamically depending on value of is_resizable() -} - -void Window::event(Core::Event& event) -{ - if (!m_client) { - ASSERT(parent()); - event.ignore(); - return; - } - - if (blocking_modal_window()) { - // We still want to handle the WindowDeactivated event below when a new modal is - // created to notify its parent window, despite it being "blocked by modal window". - if (event.type() != Event::WindowDeactivated) - return; - } - - if (static_cast<Event&>(event).is_mouse_event()) - return handle_mouse_event(static_cast<const MouseEvent&>(event)); - - switch (event.type()) { - case Event::WindowEntered: - m_client->post_message(Messages::WindowClient::WindowEntered(m_window_id)); - break; - case Event::WindowLeft: - m_client->post_message(Messages::WindowClient::WindowLeft(m_window_id)); - break; - case Event::KeyDown: - m_client->post_message( - Messages::WindowClient::KeyDown(m_window_id, - (u32) static_cast<const KeyEvent&>(event).code_point(), - (u32) static_cast<const KeyEvent&>(event).key(), - static_cast<const KeyEvent&>(event).modifiers(), - (u32) static_cast<const KeyEvent&>(event).scancode())); - break; - case Event::KeyUp: - m_client->post_message( - Messages::WindowClient::KeyUp(m_window_id, - (u32) static_cast<const KeyEvent&>(event).code_point(), - (u32) static_cast<const KeyEvent&>(event).key(), - static_cast<const KeyEvent&>(event).modifiers(), - (u32) static_cast<const KeyEvent&>(event).scancode())); - break; - case Event::WindowActivated: - m_client->post_message(Messages::WindowClient::WindowActivated(m_window_id)); - break; - case Event::WindowDeactivated: - m_client->post_message(Messages::WindowClient::WindowDeactivated(m_window_id)); - break; - case Event::WindowInputEntered: - m_client->post_message(Messages::WindowClient::WindowInputEntered(m_window_id)); - break; - case Event::WindowInputLeft: - m_client->post_message(Messages::WindowClient::WindowInputLeft(m_window_id)); - break; - case Event::WindowCloseRequest: - m_client->post_message(Messages::WindowClient::WindowCloseRequest(m_window_id)); - break; - case Event::WindowResized: - m_client->post_message(Messages::WindowClient::WindowResized(m_window_id, static_cast<const ResizeEvent&>(event).rect())); - break; - default: - break; - } -} - -void Window::set_global_cursor_tracking_enabled(bool enabled) -{ - m_global_cursor_tracking_enabled = enabled; -} - -void Window::set_visible(bool b) -{ - if (m_visible == b) - return; - m_visible = b; - - Compositor::the().invalidate_occlusions(); - if (m_visible) - invalidate(true); - else - Compositor::the().invalidate_screen(frame().rect()); -} - -void Window::invalidate(bool invalidate_frame) -{ - m_invalidated = true; - m_invalidated_all = true; - m_invalidated_frame |= invalidate_frame; - m_dirty_rects.clear(); - Compositor::the().invalidate_window(); -} - -void Window::invalidate(const Gfx::IntRect& rect, bool with_frame) -{ - if (type() == WindowType::MenuApplet) { - AppletManager::the().invalidate_applet(*this, rect); - return; - } - - if (invalidate_no_notify(rect, with_frame)) - Compositor::the().invalidate_window(); -} - -bool Window::invalidate_no_notify(const Gfx::IntRect& rect, bool with_frame) -{ - if (rect.is_empty()) - return false; - if (m_invalidated_all) { - m_invalidated_frame |= with_frame; - return false; - } - - auto outer_rect = frame().rect(); - auto inner_rect = rect; - inner_rect.move_by(position()); - // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect. - inner_rect.intersect(outer_rect); - if (inner_rect.is_empty()) - return false; - - m_invalidated = true; - m_invalidated_frame |= with_frame; - m_dirty_rects.add(inner_rect.translated(-outer_rect.location())); - return true; -} - -void Window::prepare_dirty_rects() -{ - if (m_invalidated_all) { - if (m_invalidated_frame) - m_dirty_rects = frame().rect(); - else - m_dirty_rects = rect(); - } else { - m_dirty_rects.move_by(frame().rect().location()); - } -} - -void Window::clear_dirty_rects() -{ - m_invalidated_all = false; - m_invalidated_frame = false; - m_invalidated = false; - m_dirty_rects.clear_with_capacity(); -} - -bool Window::is_active() const -{ - return WindowManager::the().active_window() == this; -} - -Window* Window::blocking_modal_window() -{ - // A window is blocked if any immediate child, or any child further - // down the chain is modal - for (auto& window : m_child_windows) { - if (window && !window->is_destroyed()) { - if (window->is_modal()) - return window; - - if (auto* blocking_window = window->blocking_modal_window()) - return blocking_window; - } - } - return nullptr; -} - -void Window::set_default_icon() -{ - m_icon = default_window_icon(); -} - -void Window::request_update(const Gfx::IntRect& rect, bool ignore_occlusion) -{ - if (rect.is_empty()) - return; - if (m_pending_paint_rects.is_empty()) { - deferred_invoke([this, ignore_occlusion](auto&) { - client()->post_paint_message(*this, ignore_occlusion); - }); - } - m_pending_paint_rects.add(rect); -} - -void Window::ensure_window_menu() -{ - if (!m_window_menu) { - m_window_menu = Menu::construct(nullptr, -1, "(Window Menu)"); - m_window_menu->set_window_menu_of(*this); - - auto minimize_item = make<MenuItem>(*m_window_menu, 1, m_minimized ? "Unminimize" : "Minimize"); - m_window_menu_minimize_item = minimize_item.ptr(); - m_window_menu->add_item(move(minimize_item)); - - auto maximize_item = make<MenuItem>(*m_window_menu, 2, m_maximized ? "Restore" : "Maximize"); - m_window_menu_maximize_item = maximize_item.ptr(); - m_window_menu->add_item(move(maximize_item)); - - m_window_menu->add_item(make<MenuItem>(*m_window_menu, MenuItem::Type::Separator)); - - auto close_item = make<MenuItem>(*m_window_menu, 3, "Close"); - m_window_menu_close_item = close_item.ptr(); - m_window_menu_close_item->set_icon(&close_icon()); - m_window_menu_close_item->set_default(true); - m_window_menu->add_item(move(close_item)); - - m_window_menu->item((int)PopupMenuItem::Minimize).set_enabled(m_minimizable); - m_window_menu->item((int)PopupMenuItem::Maximize).set_enabled(m_resizable); - - m_window_menu->on_item_activation = [&](auto& item) { - switch (item.identifier()) { - case 1: - WindowManager::the().minimize_windows(*this, !m_minimized); - if (!m_minimized) - WindowManager::the().move_to_front_and_make_active(*this); - break; - case 2: - WindowManager::the().maximize_windows(*this, !m_maximized); - WindowManager::the().move_to_front_and_make_active(*this); - break; - case 3: - request_close(); - break; - } - }; - } -} - -void Window::popup_window_menu(const Gfx::IntPoint& position, WindowMenuDefaultAction default_action) -{ - ensure_window_menu(); - if (default_action == WindowMenuDefaultAction::BasedOnWindowState) { - // When clicked on the task bar, determine the default action - if (!is_active() && !is_minimized()) - default_action = WindowMenuDefaultAction::None; - else if (is_minimized()) - default_action = WindowMenuDefaultAction::Unminimize; - else - default_action = WindowMenuDefaultAction::Minimize; - } - m_window_menu_minimize_item->set_default(default_action == WindowMenuDefaultAction::Minimize || default_action == WindowMenuDefaultAction::Unminimize); - m_window_menu_minimize_item->set_icon(m_minimized ? nullptr : &minimize_icon()); - m_window_menu_maximize_item->set_default(default_action == WindowMenuDefaultAction::Maximize || default_action == WindowMenuDefaultAction::Restore); - m_window_menu_maximize_item->set_icon(m_maximized ? &restore_icon() : &maximize_icon()); - m_window_menu_close_item->set_default(default_action == WindowMenuDefaultAction::Close); - - m_window_menu->popup(position); -} - -void Window::window_menu_activate_default() -{ - ensure_window_menu(); - m_window_menu->activate_default(); -} - -void Window::request_close() -{ - Event close_request(Event::WindowCloseRequest); - event(close_request); -} - -void Window::set_fullscreen(bool fullscreen) -{ - if (m_fullscreen == fullscreen) - return; - m_fullscreen = fullscreen; - Gfx::IntRect new_window_rect = m_rect; - if (m_fullscreen) { - m_saved_nonfullscreen_rect = m_rect; - new_window_rect = Screen::the().rect(); - } else if (!m_saved_nonfullscreen_rect.is_empty()) { - new_window_rect = m_saved_nonfullscreen_rect; - } - - Core::EventLoop::current().post_event(*this, make<ResizeEvent>(new_window_rect)); - set_rect(new_window_rect); -} - -Gfx::IntRect Window::tiled_rect(WindowTileType tiled) const -{ - int frame_width = (m_frame.rect().width() - m_rect.width()) / 2; - int title_bar_height = m_frame.title_bar_rect().height(); - int menu_height = WindowManager::the().maximized_window_rect(*this).y(); - int max_height = WindowManager::the().maximized_window_rect(*this).height(); - - switch (tiled) { - case WindowTileType::None: - return m_untiled_rect; - case WindowTileType::Left: - return Gfx::IntRect(0, - menu_height, - Screen::the().width() / 2 - frame_width, - max_height); - case WindowTileType::Right: - return Gfx::IntRect(Screen::the().width() / 2 + frame_width, - menu_height, - Screen::the().width() / 2 - frame_width, - max_height); - case WindowTileType::Top: - return Gfx::IntRect(0, - menu_height, - Screen::the().width() - frame_width, - (max_height - title_bar_height) / 2 - frame_width); - case WindowTileType::Bottom: - return Gfx::IntRect(0, - menu_height + (title_bar_height + max_height) / 2 + frame_width, - Screen::the().width() - frame_width, - (max_height - title_bar_height) / 2 - frame_width); - case WindowTileType::TopLeft: - return Gfx::IntRect(0, - menu_height, - Screen::the().width() / 2 - frame_width, - (max_height - title_bar_height) / 2 - frame_width); - case WindowTileType::TopRight: - return Gfx::IntRect(Screen::the().width() / 2 + frame_width, - menu_height, - Screen::the().width() / 2 - frame_width, - (max_height - title_bar_height) / 2 - frame_width); - case WindowTileType::BottomLeft: - return Gfx::IntRect(0, - menu_height + (title_bar_height + max_height) / 2 + frame_width, - Screen::the().width() / 2 - frame_width, - (max_height - title_bar_height) / 2); - case WindowTileType::BottomRight: - return Gfx::IntRect(Screen::the().width() / 2 + frame_width, - menu_height + (title_bar_height + max_height) / 2 + frame_width, - Screen::the().width() / 2 - frame_width, - (max_height - title_bar_height) / 2); - default: - ASSERT_NOT_REACHED(); - } -} - -void Window::set_tiled(WindowTileType tiled) -{ - if (m_tiled == tiled) - return; - - if (resize_aspect_ratio().has_value()) - return; - - m_tiled = tiled; - if (tiled != WindowTileType::None) - m_untiled_rect = m_rect; - set_rect(tiled_rect(tiled)); - Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect)); -} - -void Window::detach_client(Badge<ClientConnection>) -{ - m_client = nullptr; -} - -void Window::recalculate_rect() -{ - if (!is_resizable()) - return; - - bool send_event = true; - if (m_tiled != WindowTileType::None) { - set_rect(tiled_rect(m_tiled)); - } else if (is_maximized()) { - set_rect(WindowManager::the().maximized_window_rect(*this)); - } else if (type() == WindowType::Desktop) { - set_rect(WindowManager::the().desktop_rect()); - } else { - send_event = false; - } - - if (send_event) { - Core::EventLoop::current().post_event(*this, make<ResizeEvent>(m_rect)); - } -} - -void Window::add_child_window(Window& child_window) -{ - m_child_windows.append(child_window); -} - -void Window::add_accessory_window(Window& accessory_window) -{ - m_accessory_windows.append(accessory_window); -} - -void Window::set_parent_window(Window& parent_window) -{ - ASSERT(!m_parent_window); - m_parent_window = parent_window; - if (m_accessory) - parent_window.add_accessory_window(*this); - else - parent_window.add_child_window(*this); -} - -bool Window::is_accessory() const -{ - if (!m_accessory) - return false; - if (parent_window() != nullptr) - return true; - - // If accessory window was unparented, convert to a regular window - const_cast<Window*>(this)->set_accessory(false); - return false; -} - -bool Window::is_accessory_of(Window& window) const -{ - if (!is_accessory()) - return false; - return parent_window() == &window; -} - -void Window::modal_unparented() -{ - m_modal = false; - WindowManager::the().notify_modal_unparented(*this); -} - -bool Window::is_modal() const -{ - if (!m_modal) - return false; - if (!m_parent_window) { - const_cast<Window*>(this)->modal_unparented(); - return false; - } - return true; -} - -void Window::set_progress(int progress) -{ - if (m_progress == progress) - return; - - m_progress = progress; - WindowManager::the().notify_progress_changed(*this); -} - -bool Window::is_descendant_of(Window& window) const -{ - for (auto* parent = parent_window(); parent; parent = parent->parent_window()) { - if (parent == &window) - return true; - for (auto& accessory : parent->accessory_windows()) { - if (accessory == &window) - return true; - } - } - return false; -} - -} diff --git a/Services/WindowServer/Window.h b/Services/WindowServer/Window.h deleted file mode 100644 index 286d543cc4..0000000000 --- a/Services/WindowServer/Window.h +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/InlineLinkedList.h> -#include <AK/String.h> -#include <AK/WeakPtr.h> -#include <LibCore/Object.h> -#include <LibGfx/Bitmap.h> -#include <LibGfx/DisjointRectSet.h> -#include <LibGfx/Rect.h> -#include <WindowServer/WindowFrame.h> -#include <WindowServer/WindowType.h> - -namespace WindowServer { - -class ClientConnection; -class Cursor; -class Menu; -class MenuItem; -class MouseEvent; - -enum WMEventMask { - WindowRectChanges = 1 << 0, - WindowStateChanges = 1 << 1, - WindowIconChanges = 1 << 2, - WindowRemovals = 1 << 3, -}; - -enum class WindowTileType { - None = 0, - Left, - Right, - Top, - Bottom, - TopLeft, - TopRight, - BottomLeft, - BottomRight -}; - -enum class PopupMenuItem { - Minimize = 0, - Maximize, -}; - -enum class WindowMenuDefaultAction { - None = 0, - BasedOnWindowState, - Close, - Minimize, - Unminimize, - Maximize, - Restore -}; - -class Window final : public Core::Object - , public InlineLinkedListNode<Window> { - C_OBJECT(Window) -public: - Window(ClientConnection&, WindowType, int window_id, bool modal, bool minimizable, bool frameless, bool resizable, bool fullscreen, bool accessory, Window* parent_window = nullptr); - Window(Core::Object&, WindowType); - virtual ~Window() override; - - void popup_window_menu(const Gfx::IntPoint&, WindowMenuDefaultAction); - void window_menu_activate_default(); - void request_close(); - - unsigned wm_event_mask() const { return m_wm_event_mask; } - void set_wm_event_mask(unsigned mask) { m_wm_event_mask = mask; } - - bool is_minimized() const { return m_minimized; } - void set_minimized(bool); - - bool is_minimizable() const { return m_minimizable; } - void set_minimizable(bool); - - bool is_resizable() const { return m_resizable && !m_fullscreen; } - void set_resizable(bool); - - bool is_maximized() const { return m_maximized; } - void set_maximized(bool); - - bool is_fullscreen() const { return m_fullscreen; } - void set_fullscreen(bool); - - WindowTileType tiled() const { return m_tiled; } - void set_tiled(WindowTileType); - - bool is_occluded() const { return m_occluded; } - void set_occluded(bool); - - bool is_movable() const - { - return m_type == WindowType::Normal; - } - - WindowFrame& frame() { return m_frame; } - const WindowFrame& frame() const { return m_frame; } - - Window* blocking_modal_window(); - - bool listens_to_wm_events() const { return m_listens_to_wm_events; } - - ClientConnection* client() { return m_client; } - const ClientConnection* client() const { return m_client; } - - WindowType type() const { return m_type; } - int window_id() const { return m_window_id; } - - bool is_internal() const { return m_client_id == -1; } - i32 client_id() const { return m_client_id; } - - String title() const { return m_title; } - void set_title(const String&); - - float opacity() const { return m_opacity; } - void set_opacity(float); - - int x() const { return m_rect.x(); } - int y() const { return m_rect.y(); } - int width() const { return m_rect.width(); } - int height() const { return m_rect.height(); } - - bool is_active() const; - - bool is_visible() const { return m_visible; } - void set_visible(bool); - - bool is_modal() const; - bool is_modal_dont_unparent() const { return m_modal && m_parent_window; } - - Gfx::IntRect rect() const { return m_rect; } - void set_rect(const Gfx::IntRect&); - void set_rect(int x, int y, int width, int height) { set_rect({ x, y, width, height }); } - void set_rect_without_repaint(const Gfx::IntRect&); - - void set_taskbar_rect(const Gfx::IntRect&); - const Gfx::IntRect& taskbar_rect() const { return m_taskbar_rect; } - - void move_to(const Gfx::IntPoint& position) { set_rect({ position, size() }); } - void move_to(int x, int y) { move_to({ x, y }); } - - void move_by(const Gfx::IntPoint& delta) { set_position_without_repaint(position().translated(delta)); } - - Gfx::IntPoint position() const { return m_rect.location(); } - void set_position(const Gfx::IntPoint& position) { set_rect({ position.x(), position.y(), width(), height() }); } - void set_position_without_repaint(const Gfx::IntPoint& position) { set_rect_without_repaint({ position.x(), position.y(), width(), height() }); } - - Gfx::IntSize size() const { return m_rect.size(); } - - void invalidate(bool with_frame = true); - void invalidate(const Gfx::IntRect&, bool with_frame = false); - bool invalidate_no_notify(const Gfx::IntRect& rect, bool with_frame = false); - - void prepare_dirty_rects(); - void clear_dirty_rects(); - Gfx::DisjointRectSet& dirty_rects() { return m_dirty_rects; } - - virtual void event(Core::Event&) override; - - // Only used by WindowType::MenuApplet. Perhaps it could be a Window subclass? I don't know. - void set_rect_in_menubar(const Gfx::IntRect& rect) { m_rect_in_menubar = rect; } - const Gfx::IntRect& rect_in_menubar() const { return m_rect_in_menubar; } - - const Gfx::Bitmap* backing_store() const { return m_backing_store.ptr(); } - Gfx::Bitmap* backing_store() { return m_backing_store.ptr(); } - - void set_backing_store(RefPtr<Gfx::Bitmap>&& backing_store) - { - m_last_backing_store = move(m_backing_store); - m_backing_store = move(backing_store); - } - - void swap_backing_stores() - { - swap(m_backing_store, m_last_backing_store); - } - - Gfx::Bitmap* last_backing_store() { return m_last_backing_store.ptr(); } - - void set_global_cursor_tracking_enabled(bool); - void set_automatic_cursor_tracking_enabled(bool enabled) { m_automatic_cursor_tracking_enabled = enabled; } - bool global_cursor_tracking() const { return m_global_cursor_tracking_enabled || m_automatic_cursor_tracking_enabled; } - - bool has_alpha_channel() const { return m_has_alpha_channel; } - void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; } - - Gfx::IntSize size_increment() const { return m_size_increment; } - void set_size_increment(const Gfx::IntSize& increment) { m_size_increment = increment; } - - const Optional<Gfx::IntSize>& resize_aspect_ratio() const { return m_resize_aspect_ratio; } - void set_resize_aspect_ratio(const Optional<Gfx::IntSize>& ratio) { m_resize_aspect_ratio = ratio; } - - Gfx::IntSize base_size() const { return m_base_size; } - void set_base_size(const Gfx::IntSize& size) { m_base_size = size; } - - const Gfx::Bitmap& icon() const { return *m_icon; } - void set_icon(NonnullRefPtr<Gfx::Bitmap>&& icon) { m_icon = move(icon); } - - void set_default_icon(); - - const Cursor* cursor() const { return m_cursor.ptr(); } - void set_cursor(RefPtr<Cursor> cursor) { m_cursor = move(cursor); } - - void request_update(const Gfx::IntRect&, bool ignore_occlusion = false); - Gfx::DisjointRectSet take_pending_paint_rects() { return move(m_pending_paint_rects); } - - bool has_taskbar_rect() const { return m_have_taskbar_rect; }; - bool in_minimize_animation() const { return m_minimize_animation_step != -1; } - int minimize_animation_index() const { return m_minimize_animation_step; } - void step_minimize_animation() { m_minimize_animation_step += 1; } - void start_minimize_animation(); - void end_minimize_animation() { m_minimize_animation_step = -1; } - - Gfx::IntRect tiled_rect(WindowTileType) const; - void recalculate_rect(); - - // For InlineLinkedList. - // FIXME: Maybe make a ListHashSet and then WindowManager can just use that. - Window* m_next { nullptr }; - Window* m_prev { nullptr }; - - void detach_client(Badge<ClientConnection>); - - Window* parent_window() { return m_parent_window; } - const Window* parent_window() const { return m_parent_window; } - - void set_parent_window(Window&); - - Vector<WeakPtr<Window>>& child_windows() { return m_child_windows; } - const Vector<WeakPtr<Window>>& child_windows() const { return m_child_windows; } - - Vector<WeakPtr<Window>>& accessory_windows() { return m_accessory_windows; } - const Vector<WeakPtr<Window>>& accessory_windows() const { return m_accessory_windows; } - - bool is_descendant_of(Window&) const; - - void set_accessory(bool accessory) { m_accessory = accessory; } - bool is_accessory() const; - bool is_accessory_of(Window&) const; - - void set_frameless(bool frameless) { m_frameless = frameless; } - bool is_frameless() const { return m_frameless; } - - int progress() const { return m_progress; } - void set_progress(int); - - bool is_destroyed() const { return m_destroyed; } - void destroy(); - - bool default_positioned() const { return m_default_positioned; } - void set_default_positioned(bool p) { m_default_positioned = p; } - - bool is_invalidated() const { return m_invalidated; } - - bool is_opaque() const - { - if (opacity() < 1.0f) - return false; - if (has_alpha_channel()) - return false; - return true; - } - - Gfx::DisjointRectSet& opaque_rects() { return m_opaque_rects; } - Gfx::DisjointRectSet& transparency_rects() { return m_transparency_rects; } - Gfx::DisjointRectSet& transparency_wallpaper_rects() { return m_transparency_wallpaper_rects; } - -private: - void handle_mouse_event(const MouseEvent&); - void update_menu_item_text(PopupMenuItem item); - void update_menu_item_enabled(PopupMenuItem item); - void add_child_window(Window&); - void add_accessory_window(Window&); - void ensure_window_menu(); - void modal_unparented(); - - ClientConnection* m_client { nullptr }; - - WeakPtr<Window> m_parent_window; - Vector<WeakPtr<Window>> m_child_windows; - Vector<WeakPtr<Window>> m_accessory_windows; - - String m_title; - Gfx::IntRect m_rect; - Gfx::IntRect m_saved_nonfullscreen_rect; - Gfx::IntRect m_taskbar_rect; - Gfx::DisjointRectSet m_dirty_rects; - Gfx::DisjointRectSet m_opaque_rects; - Gfx::DisjointRectSet m_transparency_rects; - Gfx::DisjointRectSet m_transparency_wallpaper_rects; - WindowType m_type { WindowType::Normal }; - bool m_global_cursor_tracking_enabled { false }; - bool m_automatic_cursor_tracking_enabled { false }; - bool m_visible { true }; - bool m_has_alpha_channel { false }; - bool m_modal { false }; - bool m_minimizable { false }; - bool m_frameless { false }; - bool m_resizable { false }; - Optional<Gfx::IntSize> m_resize_aspect_ratio {}; - bool m_listens_to_wm_events { false }; - bool m_minimized { false }; - bool m_maximized { false }; - bool m_fullscreen { false }; - bool m_accessory { false }; - bool m_destroyed { false }; - bool m_default_positioned { false }; - bool m_have_taskbar_rect { false }; - bool m_invalidated { true }; - bool m_invalidated_all { true }; - bool m_invalidated_frame { true }; - WindowTileType m_tiled { WindowTileType::None }; - Gfx::IntRect m_untiled_rect; - bool m_occluded { false }; - RefPtr<Gfx::Bitmap> m_backing_store; - RefPtr<Gfx::Bitmap> m_last_backing_store; - int m_window_id { -1 }; - i32 m_client_id { -1 }; - float m_opacity { 1 }; - Gfx::IntSize m_size_increment; - Gfx::IntSize m_base_size; - NonnullRefPtr<Gfx::Bitmap> m_icon; - RefPtr<Cursor> m_cursor; - WindowFrame m_frame; - unsigned m_wm_event_mask { 0 }; - Gfx::DisjointRectSet m_pending_paint_rects; - Gfx::IntRect m_unmaximized_rect; - Gfx::IntRect m_rect_in_menubar; - RefPtr<Menu> m_window_menu; - MenuItem* m_window_menu_minimize_item { nullptr }; - MenuItem* m_window_menu_maximize_item { nullptr }; - MenuItem* m_window_menu_close_item { nullptr }; - int m_minimize_animation_step { -1 }; - int m_progress { -1 }; -}; - -} diff --git a/Services/WindowServer/WindowClient.ipc b/Services/WindowServer/WindowClient.ipc deleted file mode 100644 index c6ac17b022..0000000000 --- a/Services/WindowServer/WindowClient.ipc +++ /dev/null @@ -1,42 +0,0 @@ -endpoint WindowClient = 4 -{ - Paint(i32 window_id, Gfx::IntSize window_size, Vector<Gfx::IntRect> rects) =| - MouseMove(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta, bool is_drag, Vector<String> mime_types) =| - MouseDown(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta) =| - MouseDoubleClick(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta) =| - MouseUp(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta) =| - MouseWheel(i32 window_id, Gfx::IntPoint mouse_position, u32 button, u32 buttons, u32 modifiers, i32 wheel_delta) =| - WindowEntered(i32 window_id) =| - WindowLeft(i32 window_id) =| - WindowInputEntered(i32 window_id) =| - WindowInputLeft(i32 window_id) =| - KeyDown(i32 window_id, u32 code_point, u32 key, u32 modifiers, u32 scancode) =| - KeyUp(i32 window_id, u32 code_point, u32 key, u32 modifiers, u32 scancode) =| - WindowActivated(i32 window_id) =| - WindowDeactivated(i32 window_id) =| - WindowStateChanged(i32 window_id, bool minimized, bool occluded) =| - WindowCloseRequest(i32 window_id) =| - WindowResized(i32 window_id, Gfx::IntRect new_rect) =| - - MenuItemActivated(i32 menu_id, i32 identifier) =| - - ScreenRectChanged(Gfx::IntRect rect) =| - - WM_WindowRemoved(i32 wm_id, i32 client_id, i32 window_id) =| - WM_WindowStateChanged(i32 wm_id, i32 client_id, i32 window_id, i32 parent_client_id, i32 parent_window_id, bool is_active, bool is_minimized, bool is_modal, bool is_frameless, i32 window_type, [UTF8] String title, Gfx::IntRect rect, i32 progress) =| - WM_WindowIconBitmapChanged(i32 wm_id, i32 client_id, i32 window_id, i32 icon_buffer_id, Gfx::IntSize icon_size) =| - WM_WindowRectChanged(i32 wm_id, i32 client_id, i32 window_id, Gfx::IntRect rect) =| - - AsyncSetWallpaperFinished(bool success) =| - - DragAccepted() =| - DragCancelled() =| - - DragDropped(i32 window_id, Gfx::IntPoint mouse_position, [UTF8] String text, HashMap<String,ByteBuffer> mime_data) =| - - UpdateSystemTheme(i32 system_theme_buffer_id) =| - - DisplayLinkNotification() =| - - Ping() =| -} diff --git a/Services/WindowServer/WindowFrame.cpp b/Services/WindowServer/WindowFrame.cpp deleted file mode 100644 index fee0e2a939..0000000000 --- a/Services/WindowServer/WindowFrame.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ClientConnection.h" -#include <AK/Badge.h> -#include <LibGfx/Font.h> -#include <LibGfx/Painter.h> -#include <LibGfx/StylePainter.h> -#include <LibGfx/WindowTheme.h> -#include <WindowServer/Button.h> -#include <WindowServer/Compositor.h> -#include <WindowServer/Event.h> -#include <WindowServer/Window.h> -#include <WindowServer/WindowFrame.h> -#include <WindowServer/WindowManager.h> - -namespace WindowServer { - -static Gfx::WindowTheme::WindowType to_theme_window_type(WindowType type) -{ - switch (type) { - case WindowType::Normal: - return Gfx::WindowTheme::WindowType::Normal; - case WindowType::Notification: - return Gfx::WindowTheme::WindowType::Notification; - default: - return Gfx::WindowTheme::WindowType::Other; - } -} - -static Gfx::Bitmap* s_minimize_icon; -static Gfx::Bitmap* s_maximize_icon; -static Gfx::Bitmap* s_restore_icon; -static Gfx::Bitmap* s_close_icon; - -static String s_last_title_button_icons_path; - -WindowFrame::WindowFrame(Window& window) - : m_window(window) -{ - auto button = make<Button>(*this, [this](auto&) { - m_window.request_close(); - }); - m_close_button = button.ptr(); - m_buttons.append(move(button)); - - if (window.is_resizable()) { - auto button = make<Button>(*this, [this](auto&) { - WindowManager::the().maximize_windows(m_window, !m_window.is_maximized()); - }); - m_maximize_button = button.ptr(); - m_buttons.append(move(button)); - } - - if (window.is_minimizable()) { - auto button = make<Button>(*this, [this](auto&) { - WindowManager::the().minimize_windows(m_window, true); - }); - m_minimize_button = button.ptr(); - m_buttons.append(move(button)); - } - - set_button_icons(); -} - -WindowFrame::~WindowFrame() -{ -} - -void WindowFrame::set_button_icons() -{ - if (m_window.is_frameless()) - return; - - String icons_path = WindowManager::the().palette().title_button_icons_path(); - - StringBuilder full_path; - if (!s_minimize_icon || s_last_title_button_icons_path != icons_path) { - full_path.append(icons_path); - full_path.append("window-minimize.png"); - if (!(s_minimize_icon = Gfx::Bitmap::load_from_file(full_path.to_string()).leak_ref())) - s_minimize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/downward-triangle.png").leak_ref(); - full_path.clear(); - } - if (!s_maximize_icon || s_last_title_button_icons_path != icons_path) { - full_path.append(icons_path); - full_path.append("window-maximize.png"); - if (!(s_maximize_icon = Gfx::Bitmap::load_from_file(full_path.to_string()).leak_ref())) - s_maximize_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/upward-triangle.png").leak_ref(); - full_path.clear(); - } - if (!s_restore_icon || s_last_title_button_icons_path != icons_path) { - full_path.append(icons_path); - full_path.append("window-restore.png"); - if (!(s_restore_icon = Gfx::Bitmap::load_from_file(full_path.to_string()).leak_ref())) - s_restore_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-restore.png").leak_ref(); - full_path.clear(); - } - if (!s_close_icon || s_last_title_button_icons_path != icons_path) { - full_path.append(icons_path); - full_path.append("window-close.png"); - if (!(s_close_icon = Gfx::Bitmap::load_from_file(full_path.to_string()).leak_ref())) - s_close_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window-close.png").leak_ref(); - full_path.clear(); - } - - m_close_button->set_icon(*s_close_icon); - if (m_window.is_minimizable()) - m_minimize_button->set_icon(*s_minimize_icon); - if (m_window.is_resizable()) - m_maximize_button->set_icon(m_window.is_maximized() ? *s_restore_icon : *s_maximize_icon); - - s_last_title_button_icons_path = icons_path; -} - -void WindowFrame::did_set_maximized(Badge<Window>, bool maximized) -{ - ASSERT(m_maximize_button); - m_maximize_button->set_icon(maximized ? *s_restore_icon : *s_maximize_icon); -} - -Gfx::IntRect WindowFrame::title_bar_rect() const -{ - return Gfx::WindowTheme::current().title_bar_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette()); -} - -Gfx::IntRect WindowFrame::title_bar_icon_rect() const -{ - return Gfx::WindowTheme::current().title_bar_icon_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette()); -} - -Gfx::IntRect WindowFrame::title_bar_text_rect() const -{ - return Gfx::WindowTheme::current().title_bar_text_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette()); -} - -Gfx::WindowTheme::WindowState WindowFrame::window_state_for_theme() const -{ - auto& wm = WindowManager::the(); - - if (m_flash_timer && m_flash_timer->is_active()) - return m_flash_counter & 1 ? Gfx::WindowTheme::WindowState::Active : Gfx::WindowTheme::WindowState::Inactive; - - if (&m_window == wm.m_highlight_window) - return Gfx::WindowTheme::WindowState::Highlighted; - if (&m_window == wm.m_move_window) - return Gfx::WindowTheme::WindowState::Moving; - if (wm.is_active_window_or_accessory(m_window)) - return Gfx::WindowTheme::WindowState::Active; - return Gfx::WindowTheme::WindowState::Inactive; -} - -void WindowFrame::paint_notification_frame(Gfx::Painter& painter) -{ - auto palette = WindowManager::the().palette(); - Gfx::WindowTheme::current().paint_notification_frame(painter, m_window.rect(), palette, m_buttons.last().relative_rect()); -} - -void WindowFrame::paint_normal_frame(Gfx::Painter& painter) -{ - auto palette = WindowManager::the().palette(); - auto& window = m_window; - String title_text; - if (window.client() && window.client()->is_unresponsive()) { - StringBuilder builder; - builder.append(window.title()); - builder.append(" (Not responding)"); - title_text = builder.to_string(); - } else { - title_text = window.title(); - } - - auto leftmost_button_rect = m_buttons.is_empty() ? Gfx::IntRect() : m_buttons.last().relative_rect(); - Gfx::WindowTheme::current().paint_normal_frame(painter, window_state_for_theme(), m_window.rect(), title_text, m_window.icon(), palette, leftmost_button_rect); -} - -void WindowFrame::paint(Gfx::Painter& painter) -{ - if (m_window.is_frameless()) - return; - - Gfx::PainterStateSaver saver(painter); - painter.translate(rect().location()); - - if (m_window.type() == WindowType::Notification) - paint_notification_frame(painter); - else if (m_window.type() == WindowType::Normal) - paint_normal_frame(painter); - else - return; - - for (auto& button : m_buttons) { - button.paint(painter); - } -} - -static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& rect) -{ - if (window.is_frameless()) - return rect; - return Gfx::WindowTheme::current().frame_rect_for_window(to_theme_window_type(window.type()), rect, WindowManager::the().palette()); -} - -static Gfx::IntRect frame_rect_for_window(Window& window) -{ - return frame_rect_for_window(window, window.rect()); -} - -Gfx::IntRect WindowFrame::rect() const -{ - return frame_rect_for_window(m_window); -} - -void WindowFrame::invalidate_title_bar() -{ - invalidate(title_bar_rect()); -} - -void WindowFrame::invalidate(Gfx::IntRect relative_rect) -{ - auto frame_rect = rect(); - auto window_rect = m_window.rect(); - relative_rect.move_by(frame_rect.x() - window_rect.x(), frame_rect.y() - window_rect.y()); - m_window.invalidate(relative_rect, true); -} - -void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect) -{ - layout_buttons(); - - auto old_frame_rect = frame_rect_for_window(m_window, old_rect); - auto& compositor = Compositor::the(); - for (auto& dirty : old_frame_rect.shatter(rect())) - compositor.invalidate_screen(dirty); - if (!m_window.is_opaque()) - compositor.invalidate_screen(rect()); - - compositor.invalidate_occlusions(); - - WindowManager::the().notify_rect_changed(m_window, old_rect, new_rect); -} - -void WindowFrame::layout_buttons() -{ - auto button_rects = Gfx::WindowTheme::current().layout_buttons(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette(), m_buttons.size()); - for (size_t i = 0; i < m_buttons.size(); i++) - m_buttons[i].set_relative_rect(button_rects[i]); -} - -void WindowFrame::on_mouse_event(const MouseEvent& event) -{ - ASSERT(!m_window.is_fullscreen()); - - auto& wm = WindowManager::the(); - if (m_window.type() != WindowType::Normal && m_window.type() != WindowType::Notification) - return; - - if (m_window.type() == WindowType::Normal) { - if (event.type() == Event::MouseDown) - wm.move_to_front_and_make_active(m_window); - - if (m_window.blocking_modal_window()) - return; - - if (title_bar_icon_rect().contains(event.position())) { - if (event.type() == Event::MouseDown && (event.button() == MouseButton::Left || event.button() == MouseButton::Right)) { - // Manually start a potential double click. Since we're opening - // a menu, we will only receive the MouseDown event, so we - // need to record that fact. If the user subsequently clicks - // on the same area, the menu will get closed, and we will - // receive a MouseUp event, but because windows have changed - // we don't get a MouseDoubleClick event. We can however record - // this click, and when we receive the MouseUp event check if - // it would have been considered a double click, if it weren't - // for the fact that we opened and closed a window in the meanwhile - auto& wm = WindowManager::the(); - wm.start_menu_doubleclick(m_window, event); - - m_window.popup_window_menu(title_bar_rect().bottom_left().translated(rect().location()), WindowMenuDefaultAction::Close); - return; - } else if (event.type() == Event::MouseUp && event.button() == MouseButton::Left) { - // Since the MouseDown event opened a menu, another MouseUp - // from the second click outside the menu wouldn't be considered - // a double click, so let's manually check if it would otherwise - // have been be considered to be one - auto& wm = WindowManager::the(); - if (wm.is_menu_doubleclick(m_window, event)) { - // It is a double click, so perform activate the default item - m_window.window_menu_activate_default(); - } - return; - } - } - } - - // This is slightly hackish, but expand the title bar rect by two pixels downwards, - // so that mouse events between the title bar and window contents don't act like - // mouse events on the border. - auto adjusted_title_bar_rect = title_bar_rect(); - adjusted_title_bar_rect.set_height(adjusted_title_bar_rect.height() + 2); - - if (adjusted_title_bar_rect.contains(event.position())) { - wm.clear_resize_candidate(); - - if (event.type() == Event::MouseDown) - wm.move_to_front_and_make_active(m_window); - - for (auto& button : m_buttons) { - if (button.relative_rect().contains(event.position())) - return button.on_mouse_event(event.translated(-button.relative_rect().location())); - } - if (event.type() == Event::MouseDown) { - if (m_window.type() == WindowType::Normal && event.button() == MouseButton::Right) { - auto default_action = m_window.is_maximized() ? WindowMenuDefaultAction::Restore : WindowMenuDefaultAction::Maximize; - m_window.popup_window_menu(event.position().translated(rect().location()), default_action); - return; - } - if (m_window.is_movable() && event.button() == MouseButton::Left) - wm.start_window_move(m_window, event.translated(rect().location())); - } - return; - } - - if (m_window.is_resizable() && event.type() == Event::MouseMove && event.buttons() == 0) { - constexpr ResizeDirection direction_for_hot_area[3][3] = { - { ResizeDirection::UpLeft, ResizeDirection::Up, ResizeDirection::UpRight }, - { ResizeDirection::Left, ResizeDirection::None, ResizeDirection::Right }, - { ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight }, - }; - Gfx::IntRect outer_rect = { {}, rect().size() }; - ASSERT(outer_rect.contains(event.position())); - int window_relative_x = event.x() - outer_rect.x(); - int window_relative_y = event.y() - outer_rect.y(); - int hot_area_row = min(2, window_relative_y / (outer_rect.height() / 3)); - int hot_area_column = min(2, window_relative_x / (outer_rect.width() / 3)); - wm.set_resize_candidate(m_window, direction_for_hot_area[hot_area_row][hot_area_column]); - Compositor::the().invalidate_cursor(); - return; - } - - if (m_window.is_resizable() && event.type() == Event::MouseDown && event.button() == MouseButton::Left) - wm.start_window_resize(m_window, event.translated(rect().location())); -} - -void WindowFrame::start_flash_animation() -{ - if (!m_flash_timer) { - m_flash_timer = Core::Timer::construct(100, [this] { - ASSERT(m_flash_counter); - invalidate_title_bar(); - if (!--m_flash_counter) - m_flash_timer->stop(); - }); - } - m_flash_counter = 8; - m_flash_timer->start(); -} - -} diff --git a/Services/WindowServer/WindowFrame.h b/Services/WindowServer/WindowFrame.h deleted file mode 100644 index ace32b8c7c..0000000000 --- a/Services/WindowServer/WindowFrame.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Forward.h> -#include <AK/NonnullOwnPtrVector.h> -#include <AK/RefPtr.h> -#include <LibCore/Forward.h> -#include <LibGfx/Forward.h> -#include <LibGfx/WindowTheme.h> - -namespace WindowServer { - -class Button; -class MouseEvent; -class Window; - -class WindowFrame { -public: - WindowFrame(Window&); - ~WindowFrame(); - - Gfx::IntRect rect() const; - void paint(Gfx::Painter&); - void on_mouse_event(const MouseEvent&); - void notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect); - void invalidate_title_bar(); - void invalidate(Gfx::IntRect relative_rect); - - Gfx::IntRect title_bar_rect() const; - Gfx::IntRect title_bar_icon_rect() const; - Gfx::IntRect title_bar_text_rect() const; - - void did_set_maximized(Badge<Window>, bool); - - void layout_buttons(); - void set_button_icons(); - - void start_flash_animation(); - -private: - void paint_notification_frame(Gfx::Painter&); - void paint_normal_frame(Gfx::Painter&); - - Gfx::WindowTheme::WindowState window_state_for_theme() const; - - Window& m_window; - NonnullOwnPtrVector<Button> m_buttons; - Button* m_close_button { nullptr }; - Button* m_maximize_button { nullptr }; - Button* m_minimize_button { nullptr }; - - RefPtr<Core::Timer> m_flash_timer; - size_t m_flash_counter { 0 }; -}; - -} diff --git a/Services/WindowServer/WindowManager.cpp b/Services/WindowServer/WindowManager.cpp deleted file mode 100644 index aa072fb0b6..0000000000 --- a/Services/WindowServer/WindowManager.cpp +++ /dev/null @@ -1,1509 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "WindowManager.h" -#include "Compositor.h" -#include "EventLoop.h" -#include "Menu.h" -#include "MenuBar.h" -#include "MenuItem.h" -#include "Screen.h" -#include "Window.h" -#include <AK/LogStream.h> -#include <AK/SharedBuffer.h> -#include <AK/StdLibExtras.h> -#include <AK/Vector.h> -#include <LibGfx/CharacterBitmap.h> -#include <LibGfx/Font.h> -#include <LibGfx/Painter.h> -#include <LibGfx/StylePainter.h> -#include <LibGfx/SystemTheme.h> -#include <WindowServer/AppletManager.h> -#include <WindowServer/Button.h> -#include <WindowServer/ClientConnection.h> -#include <WindowServer/Cursor.h> -#include <WindowServer/WindowClientEndpoint.h> -#include <errno.h> -#include <serenity.h> -#include <stdio.h> -#include <time.h> -#include <unistd.h> - -//#define WINDOWMANAGER_DEBUG -//#define RESIZE_DEBUG -//#define MOVE_DEBUG -//#define DOUBLECLICK_DEBUG - -namespace WindowServer { - -static WindowManager* s_the; - -WindowManager& WindowManager::the() -{ - ASSERT(s_the); - return *s_the; -} - -WindowManager::WindowManager(const Gfx::PaletteImpl& palette) - : m_palette(palette) -{ - s_the = this; - - reload_config(false); - - Compositor::the().did_construct_window_manager({}); -} - -WindowManager::~WindowManager() -{ -} - -NonnullRefPtr<Cursor> WindowManager::get_cursor(const String& name) -{ - static const auto s_default_cursor_path = "/res/cursors/arrow.x2y2.png"; - auto path = m_config->read_entry("Cursor", name, s_default_cursor_path); - auto gb = Gfx::Bitmap::load_from_file(path); - if (gb) - return Cursor::create(*gb, path); - return Cursor::create(*Gfx::Bitmap::load_from_file(s_default_cursor_path), s_default_cursor_path); -} - -void WindowManager::reload_config(bool set_screen) -{ - m_config = Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini"); - - m_double_click_speed = m_config->read_num_entry("Input", "DoubleClickSpeed", 250); - - if (set_screen) { - set_resolution(m_config->read_num_entry("Screen", "Width", 1920), m_config->read_num_entry("Screen", "Height", 1080)); - } - - m_hidden_cursor = get_cursor("Hidden"); - m_arrow_cursor = get_cursor("Arrow"); - m_hand_cursor = get_cursor("Hand"); - m_help_cursor = get_cursor("Help"); - m_resize_horizontally_cursor = get_cursor("ResizeH"); - m_resize_vertically_cursor = get_cursor("ResizeV"); - m_resize_diagonally_tlbr_cursor = get_cursor("ResizeDTLBR"); - m_resize_diagonally_bltr_cursor = get_cursor("ResizeDBLTR"); - m_resize_column_cursor = get_cursor("ResizeColumn"); - m_resize_row_cursor = get_cursor("ResizeRow"); - m_i_beam_cursor = get_cursor("IBeam"); - m_disallowed_cursor = get_cursor("Disallowed"); - m_move_cursor = get_cursor("Move"); - m_drag_cursor = get_cursor("Drag"); - m_wait_cursor = get_cursor("Wait"); - m_crosshair_cursor = get_cursor("Crosshair"); -} - -const Gfx::Font& WindowManager::font() const -{ - return Gfx::FontDatabase::default_font(); -} - -const Gfx::Font& WindowManager::window_title_font() const -{ - return Gfx::FontDatabase::default_bold_font(); -} - -bool WindowManager::set_resolution(int width, int height) -{ - bool success = Compositor::the().set_resolution(width, height); - MenuManager::the().set_needs_window_resize(); - ClientConnection::for_each_client([&](ClientConnection& client) { - client.notify_about_new_screen_rect(Screen::the().rect()); - }); - if (success) { - for_each_window([](Window& window) { - window.recalculate_rect(); - return IterationDecision::Continue; - }); - } - if (m_config) { - if (success) { - dbg() << "Saving resolution: " << Gfx::IntSize(width, height) << " to config file at " << m_config->file_name(); - m_config->write_num_entry("Screen", "Width", width); - m_config->write_num_entry("Screen", "Height", height); - m_config->sync(); - } else { - dbg() << "Saving fallback resolution: " << resolution() << " to config file at " << m_config->file_name(); - m_config->write_num_entry("Screen", "Width", resolution().width()); - m_config->write_num_entry("Screen", "Height", resolution().height()); - m_config->sync(); - } - } - return success; -} - -Gfx::IntSize WindowManager::resolution() const -{ - return Screen::the().size(); -} - -void WindowManager::set_acceleration_factor(double factor) -{ - Screen::the().set_acceleration_factor(factor); - dbgln("Saving acceleration factor {} to config file at {}", factor, m_config->file_name()); - m_config->write_entry("Mouse", "AccelerationFactor", String::formatted("{}", factor)); - m_config->sync(); -} - -void WindowManager::set_scroll_step_size(unsigned step_size) -{ - Screen::the().set_scroll_step_size(step_size); - dbgln("Saving scroll step size {} to config file at {}", step_size, m_config->file_name()); - m_config->write_entry("Mouse", "ScrollStepSize", String::number(step_size)); - m_config->sync(); -} - -void WindowManager::add_window(Window& window) -{ - bool is_first_window = m_windows_in_order.is_empty(); - - m_windows_in_order.append(&window); - - if (window.is_fullscreen()) { - Core::EventLoop::current().post_event(window, make<ResizeEvent>(Screen::the().rect())); - window.set_rect(Screen::the().rect()); - } - - if (window.type() != WindowType::Desktop || is_first_window) - set_active_window(&window); - - if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher) - m_switcher.refresh(); - - Compositor::the().invalidate_occlusions(); - - if (window.listens_to_wm_events()) { - for_each_window([&](Window& other_window) { - if (&window != &other_window) { - tell_wm_listener_about_window(window, other_window); - tell_wm_listener_about_window_icon(window, other_window); - } - return IterationDecision::Continue; - }); - } - - tell_wm_listeners_window_state_changed(window); -} - -void WindowManager::move_to_front_and_make_active(Window& window) -{ - auto move_window_to_front = [&](Window& wnd, bool make_active, bool make_input) { - if (wnd.is_accessory()) { - auto* parent = wnd.parent_window(); - do_move_to_front(*parent, true, false); - make_active = false; - - for (auto& accessory_window : parent->accessory_windows()) { - if (accessory_window && accessory_window.ptr() != &wnd) - do_move_to_front(*accessory_window, false, false); - } - } - - do_move_to_front(wnd, make_active, make_input); - }; - - // If a window that is currently blocked by a modal child is being - // brought to the front, bring the entire stack of modal windows - // to the front and activate the modal window. Also set the - // active input window to that same window (which would pull - // active input from any accessory window) - for_each_window_in_modal_stack(window, [&](auto& w, bool is_stack_top) { - move_window_to_front(w, is_stack_top, is_stack_top); - return IterationDecision::Continue; - }); - - Compositor::the().invalidate_occlusions(); -} - -void WindowManager::do_move_to_front(Window& window, bool make_active, bool make_input) -{ - if (m_windows_in_order.tail() != &window) - window.invalidate(); - m_windows_in_order.remove(&window); - m_windows_in_order.append(&window); - - if (make_active) - set_active_window(&window, make_input); - - if (m_switcher.is_visible()) { - m_switcher.refresh(); - if (!window.is_accessory()) { - m_switcher.select_window(window); - set_highlight_window(&window); - } - } - - for (auto& child_window : window.child_windows()) { - if (child_window) - do_move_to_front(*child_window, make_active, make_input); - } -} - -void WindowManager::remove_window(Window& window) -{ - m_windows_in_order.remove(&window); - auto* active = active_window(); - auto* active_input = active_input_window(); - if (active == &window || active_input == &window || (active && window.is_descendant_of(*active)) || (active_input && active_input != active && window.is_descendant_of(*active_input))) - pick_new_active_window(&window); - - Compositor::the().invalidate_screen(window.frame().rect()); - - if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher) - m_switcher.refresh(); - - Compositor::the().invalidate_occlusions(); - - for_each_window_listening_to_wm_events([&window](Window& listener) { - if (!(listener.wm_event_mask() & WMEventMask::WindowRemovals)) - return IterationDecision::Continue; - if (!window.is_internal() && !window.is_modal()) - listener.client()->post_message(Messages::WindowClient::WM_WindowRemoved(listener.window_id(), window.client_id(), window.window_id())); - return IterationDecision::Continue; - }); -} - -void WindowManager::tell_wm_listener_about_window(Window& listener, Window& window) -{ - if (!(listener.wm_event_mask() & WMEventMask::WindowStateChanges)) - return; - if (window.is_internal()) - return; - auto* parent = window.parent_window(); - listener.client()->post_message(Messages::WindowClient::WM_WindowStateChanged(listener.window_id(), window.client_id(), window.window_id(), parent ? parent->client_id() : -1, parent ? parent->window_id() : -1, window.is_active(), window.is_minimized(), window.is_modal_dont_unparent(), window.is_frameless(), (i32)window.type(), window.title(), window.rect(), window.progress())); -} - -void WindowManager::tell_wm_listener_about_window_rect(Window& listener, Window& window) -{ - if (!(listener.wm_event_mask() & WMEventMask::WindowRectChanges)) - return; - if (window.is_internal()) - return; - listener.client()->post_message(Messages::WindowClient::WM_WindowRectChanged(listener.window_id(), window.client_id(), window.window_id(), window.rect())); -} - -void WindowManager::tell_wm_listener_about_window_icon(Window& listener, Window& window) -{ - if (!(listener.wm_event_mask() & WMEventMask::WindowIconChanges)) - return; - if (window.is_internal()) - return; - if (window.icon().shbuf_id() == -1) - return; -#ifdef WINDOWMANAGER_DEBUG - dbg() << "WindowServer: Sharing icon buffer " << window.icon().shbuf_id() << " with PID " << listener.client()->client_pid(); -#endif - if (shbuf_allow_pid(window.icon().shbuf_id(), listener.client()->client_pid()) < 0) { - ASSERT_NOT_REACHED(); - } - listener.client()->post_message(Messages::WindowClient::WM_WindowIconBitmapChanged(listener.window_id(), window.client_id(), window.window_id(), window.icon().shbuf_id(), window.icon().size())); -} - -void WindowManager::tell_wm_listeners_window_state_changed(Window& window) -{ - for_each_window_listening_to_wm_events([&](Window& listener) { - tell_wm_listener_about_window(listener, window); - return IterationDecision::Continue; - }); -} - -void WindowManager::tell_wm_listeners_window_icon_changed(Window& window) -{ - for_each_window_listening_to_wm_events([&](Window& listener) { - tell_wm_listener_about_window_icon(listener, window); - return IterationDecision::Continue; - }); -} - -void WindowManager::tell_wm_listeners_window_rect_changed(Window& window) -{ - for_each_window_listening_to_wm_events([&](Window& listener) { - tell_wm_listener_about_window_rect(listener, window); - return IterationDecision::Continue; - }); -} - -void WindowManager::notify_title_changed(Window& window) -{ - if (window.type() != WindowType::Normal) - return; -#ifdef WINDOWMANAGER_DEBUG - dbg() << "[WM] Window{" << &window << "} title set to \"" << window.title() << '"'; -#endif - if (m_switcher.is_visible()) - m_switcher.refresh(); - - tell_wm_listeners_window_state_changed(window); -} - -void WindowManager::notify_modal_unparented(Window& window) -{ - if (window.type() != WindowType::Normal) - return; -#ifdef WINDOWMANAGER_DEBUG - dbg() << "[WM] Modal Window{" << &window << "} was unparented"; -#endif - if (m_switcher.is_visible()) - m_switcher.refresh(); - - tell_wm_listeners_window_state_changed(window); -} - -void WindowManager::notify_rect_changed(Window& window, [[maybe_unused]] const Gfx::IntRect& old_rect, [[maybe_unused]] const Gfx::IntRect& new_rect) -{ -#ifdef RESIZE_DEBUG - dbg() << "[WM] Window " << &window << " rect changed " << old_rect << " -> " << new_rect; -#endif - if (m_switcher.is_visible() && window.type() != WindowType::WindowSwitcher) - m_switcher.refresh(); - - tell_wm_listeners_window_rect_changed(window); - - if (window.type() == WindowType::MenuApplet) - AppletManager::the().calculate_applet_rects(MenuManager::the().window()); - - MenuManager::the().refresh(); -} - -void WindowManager::notify_opacity_changed(Window&) -{ - Compositor::the().invalidate_occlusions(); -} - -void WindowManager::notify_minimization_state_changed(Window& window) -{ - tell_wm_listeners_window_state_changed(window); - - if (window.client()) - window.client()->post_message(Messages::WindowClient::WindowStateChanged(window.window_id(), window.is_minimized(), window.is_occluded())); - - if (window.is_active() && window.is_minimized()) - pick_new_active_window(&window); -} - -void WindowManager::notify_occlusion_state_changed(Window& window) -{ - if (window.client()) - window.client()->post_message(Messages::WindowClient::WindowStateChanged(window.window_id(), window.is_minimized(), window.is_occluded())); -} - -void WindowManager::notify_progress_changed(Window& window) -{ - tell_wm_listeners_window_state_changed(window); -} - -bool WindowManager::pick_new_active_window(Window* previous_active) -{ - bool new_window_picked = false; - Window* first_candidate = nullptr; - for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, [&](Window& candidate) { - if (candidate.is_destroyed()) - return IterationDecision::Continue; - if (previous_active != first_candidate) - first_candidate = &candidate; - if ((!previous_active && !candidate.is_accessory()) || (previous_active && !candidate.is_accessory_of(*previous_active))) { - set_active_window(&candidate); - new_window_picked = true; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - if (!new_window_picked) { - set_active_window(first_candidate); - new_window_picked = first_candidate != nullptr; - } - return new_window_picked; -} - -void WindowManager::start_window_move(Window& window, const MouseEvent& event) -{ -#ifdef MOVE_DEBUG - dbg() << "[WM] Begin moving Window{" << &window << "}"; -#endif - move_to_front_and_make_active(window); - m_move_window = window; - m_move_window->set_default_positioned(false); - m_move_origin = event.position(); - m_move_window_origin = window.position(); - window.invalidate(); -} - -void WindowManager::start_window_resize(Window& window, const Gfx::IntPoint& position, MouseButton button) -{ - move_to_front_and_make_active(window); - constexpr ResizeDirection direction_for_hot_area[3][3] = { - { ResizeDirection::UpLeft, ResizeDirection::Up, ResizeDirection::UpRight }, - { ResizeDirection::Left, ResizeDirection::None, ResizeDirection::Right }, - { ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight }, - }; - Gfx::IntRect outer_rect = window.frame().rect(); - if (!outer_rect.contains(position)) { - // FIXME: This used to be an ASSERT but crashing WindowServer over this seems silly. - dbg() << "FIXME: !outer_rect.contains(position): outer_rect=" << outer_rect << ", position=" << position; - } - int window_relative_x = position.x() - outer_rect.x(); - int window_relative_y = position.y() - outer_rect.y(); - int hot_area_row = min(2, window_relative_y / (outer_rect.height() / 3)); - int hot_area_column = min(2, window_relative_x / (outer_rect.width() / 3)); - m_resize_direction = direction_for_hot_area[hot_area_row][hot_area_column]; - if (m_resize_direction == ResizeDirection::None) { - ASSERT(!m_resize_window); - return; - } - -#ifdef RESIZE_DEBUG - dbg() << "[WM] Begin resizing Window{" << &window << "}"; -#endif - m_resizing_mouse_button = button; - m_resize_window = window; - m_resize_origin = position; - m_resize_window_original_rect = window.rect(); - - m_active_input_tracking_window = nullptr; - - window.invalidate(); - - if (hot_area_row == 0 || hot_area_column == 0) { - m_resize_window->set_default_positioned(false); - } -} - -void WindowManager::start_window_resize(Window& window, const MouseEvent& event) -{ - start_window_resize(window, event.position(), event.button()); -} - -bool WindowManager::process_ongoing_window_move(MouseEvent& event, Window*& hovered_window) -{ - if (!m_move_window) - return false; - if (event.type() == Event::MouseUp && event.button() == MouseButton::Left) { -#ifdef MOVE_DEBUG - dbg() << "[WM] Finish moving Window{" << m_move_window << "}"; -#endif - - m_move_window->invalidate(); - if (m_move_window->rect().contains(event.position())) - hovered_window = m_move_window; - if (m_move_window->is_resizable()) { - process_event_for_doubleclick(*m_move_window, event); - if (event.type() == Event::MouseDoubleClick) { -#if defined(DOUBLECLICK_DEBUG) - dbgln("[WM] Click up became doubleclick!"); -#endif - m_move_window->set_maximized(!m_move_window->is_maximized()); - } - } - m_move_window = nullptr; - return true; - } - if (event.type() == Event::MouseMove) { - -#ifdef MOVE_DEBUG - dbg() << "[WM] Moving, origin: " << m_move_origin << ", now: " << event.position(); - if (m_move_window->is_maximized()) { - dbgln(" [!] The window is still maximized. Not moving yet."); - } - -#endif - - const int tiling_deadzone = 10; - const int secondary_deadzone = 2; - - if (m_move_window->is_maximized()) { - auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin); - // dbg() << "[WM] " << pixels_moved_from_start << " moved since start of window move"; - if (pixels_moved_from_start > 5) { - // dbgln("[WM] de-maximizing window"); - m_move_origin = event.position(); - if (m_move_origin.y() <= secondary_deadzone) - return true; - auto width_before_resize = m_move_window->width(); - m_move_window->set_maximized(false); - m_move_window->move_to(m_move_origin.x() - (m_move_window->width() * ((float)m_move_origin.x() / width_before_resize)), m_move_origin.y()); - m_move_window_origin = m_move_window->position(); - } - } else { - bool is_resizable = m_move_window->is_resizable(); - auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin); - auto desktop = desktop_rect(); - - if (is_resizable && event.x() <= tiling_deadzone) { - if (event.y() <= tiling_deadzone + desktop.top()) - m_move_window->set_tiled(WindowTileType::TopLeft); - else if (event.y() >= desktop.height() - tiling_deadzone) - m_move_window->set_tiled(WindowTileType::BottomLeft); - else - m_move_window->set_tiled(WindowTileType::Left); - } else if (is_resizable && event.x() >= Screen::the().width() - tiling_deadzone) { - if (event.y() <= tiling_deadzone + desktop.top()) - m_move_window->set_tiled(WindowTileType::TopRight); - else if (event.y() >= desktop.height() - tiling_deadzone) - m_move_window->set_tiled(WindowTileType::BottomRight); - else - m_move_window->set_tiled(WindowTileType::Right); - } else if (is_resizable && event.y() <= secondary_deadzone + desktop.top()) { - m_move_window->set_tiled(WindowTileType::Top); - } else if (is_resizable && event.y() >= desktop.bottom() - secondary_deadzone) { - m_move_window->set_tiled(WindowTileType::Bottom); - } else if (pixels_moved_from_start > 5 || m_move_window->tiled() == WindowTileType::None) { - m_move_window->set_tiled(WindowTileType::None); - Gfx::IntPoint pos = m_move_window_origin.translated(event.position() - m_move_origin); - m_move_window->set_position_without_repaint(pos); - if (m_move_window->rect().contains(event.position())) - hovered_window = m_move_window; - } - return true; - } - } - return false; -} - -bool WindowManager::process_ongoing_window_resize(const MouseEvent& event, Window*& hovered_window) -{ - if (!m_resize_window) - return false; - - if (event.type() == Event::MouseUp && event.button() == m_resizing_mouse_button) { -#ifdef RESIZE_DEBUG - dbg() << "[WM] Finish resizing Window{" << m_resize_window << "}"; -#endif - Core::EventLoop::current().post_event(*m_resize_window, make<ResizeEvent>(m_resize_window->rect())); - m_resize_window->invalidate(); - if (m_resize_window->rect().contains(event.position())) - hovered_window = m_resize_window; - m_resize_window = nullptr; - m_resizing_mouse_button = MouseButton::None; - return true; - } - - if (event.type() != Event::MouseMove) - return false; - - int diff_x = event.x() - m_resize_origin.x(); - int diff_y = event.y() - m_resize_origin.y(); - - int change_w = 0; - int change_h = 0; - - switch (m_resize_direction) { - case ResizeDirection::DownRight: - change_w = diff_x; - change_h = diff_y; - break; - case ResizeDirection::Right: - change_w = diff_x; - break; - case ResizeDirection::UpRight: - change_w = diff_x; - change_h = -diff_y; - break; - case ResizeDirection::Up: - change_h = -diff_y; - break; - case ResizeDirection::UpLeft: - change_w = -diff_x; - change_h = -diff_y; - break; - case ResizeDirection::Left: - change_w = -diff_x; - break; - case ResizeDirection::DownLeft: - change_w = -diff_x; - change_h = diff_y; - break; - case ResizeDirection::Down: - change_h = diff_y; - break; - default: - ASSERT_NOT_REACHED(); - } - - auto new_rect = m_resize_window_original_rect; - - // First, size the new rect. - Gfx::IntSize minimum_size { 50, 50 }; - - new_rect.set_width(max(minimum_size.width(), new_rect.width() + change_w)); - new_rect.set_height(max(minimum_size.height(), new_rect.height() + change_h)); - - if (!m_resize_window->size_increment().is_null()) { - int horizontal_incs = (new_rect.width() - m_resize_window->base_size().width()) / m_resize_window->size_increment().width(); - new_rect.set_width(m_resize_window->base_size().width() + horizontal_incs * m_resize_window->size_increment().width()); - int vertical_incs = (new_rect.height() - m_resize_window->base_size().height()) / m_resize_window->size_increment().height(); - new_rect.set_height(m_resize_window->base_size().height() + vertical_incs * m_resize_window->size_increment().height()); - } - - if (m_resize_window->resize_aspect_ratio().has_value()) { - auto& ratio = m_resize_window->resize_aspect_ratio().value(); - if (abs(change_w) > abs(change_h)) { - new_rect.set_height(new_rect.width() * ratio.height() / ratio.width()); - } else { - new_rect.set_width(new_rect.height() * ratio.width() / ratio.height()); - } - } - - // Second, set its position so that the sides of the window - // that end up moving are the same ones as the user is dragging, - // no matter which part of the logic above caused us to decide - // to resize by this much. - switch (m_resize_direction) { - case ResizeDirection::DownRight: - case ResizeDirection::Right: - case ResizeDirection::Down: - break; - case ResizeDirection::Left: - case ResizeDirection::Up: - case ResizeDirection::UpLeft: - new_rect.set_right_without_resize(m_resize_window_original_rect.right()); - new_rect.set_bottom_without_resize(m_resize_window_original_rect.bottom()); - break; - case ResizeDirection::UpRight: - new_rect.set_bottom_without_resize(m_resize_window_original_rect.bottom()); - break; - case ResizeDirection::DownLeft: - new_rect.set_right_without_resize(m_resize_window_original_rect.right()); - break; - default: - ASSERT_NOT_REACHED(); - } - - if (new_rect.contains(event.position())) - hovered_window = m_resize_window; - - if (m_resize_window->rect() == new_rect) - return true; -#ifdef RESIZE_DEBUG - dbg() << "[WM] Resizing, original: " << m_resize_window_original_rect << ", now: " << new_rect; -#endif - m_resize_window->set_rect(new_rect); - Core::EventLoop::current().post_event(*m_resize_window, make<ResizeEvent>(new_rect)); - return true; -} - -bool WindowManager::process_ongoing_drag(MouseEvent& event, Window*& hovered_window) -{ - if (!m_dnd_client) - return false; - - if (event.type() == Event::MouseMove) { - // We didn't let go of the drag yet, see if we should send some drag move events.. - for_each_visible_window_from_front_to_back([&](Window& window) { - if (!window.rect().contains(event.position())) - return IterationDecision::Continue; - hovered_window = &window; - auto translated_event = event.translated(-window.position()); - translated_event.set_drag(true); - translated_event.set_mime_data(*m_dnd_mime_data); - deliver_mouse_event(window, translated_event); - return IterationDecision::Break; - }); - } - - if (!(event.type() == Event::MouseUp && event.button() == MouseButton::Left)) - return true; - - hovered_window = nullptr; - for_each_visible_window_from_front_to_back([&](auto& window) { - if (window.frame().rect().contains(event.position())) { - hovered_window = &window; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - - if (hovered_window) { - m_dnd_client->post_message(Messages::WindowClient::DragAccepted()); - if (hovered_window->client()) { - auto translated_event = event.translated(-hovered_window->position()); - hovered_window->client()->post_message(Messages::WindowClient::DragDropped(hovered_window->window_id(), translated_event.position(), m_dnd_text, m_dnd_mime_data->all_data())); - } - } else { - m_dnd_client->post_message(Messages::WindowClient::DragCancelled()); - } - - end_dnd_drag(); - return true; -} - -void WindowManager::set_cursor_tracking_button(Button* button) -{ - m_cursor_tracking_button = button; -} - -auto WindowManager::DoubleClickInfo::metadata_for_button(MouseButton button) const -> const ClickMetadata& -{ - switch (button) { - case MouseButton::Left: - return m_left; - case MouseButton::Right: - return m_right; - case MouseButton::Middle: - return m_middle; - case MouseButton::Back: - return m_back; - case MouseButton::Forward: - return m_forward; - default: - ASSERT_NOT_REACHED(); - } -} - -auto WindowManager::DoubleClickInfo::metadata_for_button(MouseButton button) -> ClickMetadata& -{ - switch (button) { - case MouseButton::Left: - return m_left; - case MouseButton::Right: - return m_right; - case MouseButton::Middle: - return m_middle; - case MouseButton::Back: - return m_back; - case MouseButton::Forward: - return m_forward; - default: - ASSERT_NOT_REACHED(); - } -} - -// #define DOUBLECLICK_DEBUG - -bool WindowManager::is_considered_doubleclick(const MouseEvent& event, const DoubleClickInfo::ClickMetadata& metadata) const -{ - int elapsed_since_last_click = metadata.clock.elapsed(); - if (elapsed_since_last_click < m_double_click_speed) { - auto diff = event.position() - metadata.last_position; - auto distance_travelled_squared = diff.x() * diff.x() + diff.y() * diff.y(); - if (distance_travelled_squared <= (m_max_distance_for_double_click * m_max_distance_for_double_click)) - return true; - } - return false; -} - -void WindowManager::start_menu_doubleclick(Window& window, const MouseEvent& event) -{ - // This is a special case. Basically, we're trying to determine whether - // double clicking on the window menu icon happened. In this case, the - // WindowFrame only receives a MouseDown event, and since the window - // menu popus up, it does not see the MouseUp event. But, if they subsequently - // click there again, the menu is closed and we receive a MouseUp event. - // So, in order to be able to detect a double click when a menu is being - // opened by the MouseDown event, we need to consider the MouseDown event - // as a potential double-click trigger - ASSERT(event.type() == Event::MouseDown); - - auto& metadata = m_double_click_info.metadata_for_button(event.button()); - if (&window != m_double_click_info.m_clicked_window) { - // we either haven't clicked anywhere, or we haven't clicked on this - // window. set the current click window, and reset the timers. -#if defined(DOUBLECLICK_DEBUG) - dbg() << "Initial mousedown on window " << &window << " for menu (previous was " << m_double_click_info.m_clicked_window << ')'; -#endif - m_double_click_info.m_clicked_window = window; - m_double_click_info.reset(); - } - - metadata.last_position = event.position(); - metadata.clock.start(); -} - -bool WindowManager::is_menu_doubleclick(Window& window, const MouseEvent& event) const -{ - ASSERT(event.type() == Event::MouseUp); - - if (&window != m_double_click_info.m_clicked_window) - return false; - - auto& metadata = m_double_click_info.metadata_for_button(event.button()); - if (!metadata.clock.is_valid()) - return false; - - return is_considered_doubleclick(event, metadata); -} - -void WindowManager::process_event_for_doubleclick(Window& window, MouseEvent& event) -{ - // We only care about button presses (because otherwise it's not a doubleclick, duh!) - ASSERT(event.type() == Event::MouseUp); - - if (&window != m_double_click_info.m_clicked_window) { - // we either haven't clicked anywhere, or we haven't clicked on this - // window. set the current click window, and reset the timers. -#if defined(DOUBLECLICK_DEBUG) - dbg() << "Initial mouseup on window " << &window << " (previous was " << m_double_click_info.m_clicked_window << ')'; -#endif - m_double_click_info.m_clicked_window = window; - m_double_click_info.reset(); - } - - auto& metadata = m_double_click_info.metadata_for_button(event.button()); - - if (!metadata.clock.is_valid() || !is_considered_doubleclick(event, metadata)) { - // either the clock is invalid because we haven't clicked on this - // button on this window yet, so there's nothing to do, or this - // isn't considered to be a double click. either way, restart the - // clock - metadata.clock.start(); - } else { -#if defined(DOUBLECLICK_DEBUG) - dbg() << "Transforming MouseUp to MouseDoubleClick (" << metadata.clock.elapsed() << " < " << m_double_click_speed << ")!"; -#endif - event = MouseEvent(Event::MouseDoubleClick, event.position(), event.buttons(), event.button(), event.modifiers(), event.wheel_delta()); - // invalidate this now we've delivered a doubleclick, otherwise - // tripleclick will deliver two doubleclick events (incorrectly). - metadata.clock = {}; - } - - metadata.last_position = event.position(); -} - -void WindowManager::deliver_mouse_event(Window& window, MouseEvent& event) -{ - window.dispatch_event(event); - if (event.type() == Event::MouseUp) { - process_event_for_doubleclick(window, event); - if (event.type() == Event::MouseDoubleClick) - window.dispatch_event(event); - } -} - -void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_window) -{ - HashTable<Window*> windows_who_received_mouse_event_due_to_cursor_tracking; - - // We need to process ongoing drag events first. Otherwise, global tracking - // and dnd collides, leading to duplicate GUI::DragOperation instances - if (process_ongoing_drag(event, hovered_window)) - return; - - for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { - if (!window->global_cursor_tracking() || !window->is_visible() || window->is_minimized() || window->blocking_modal_window()) - continue; - windows_who_received_mouse_event_due_to_cursor_tracking.set(window); - auto translated_event = event.translated(-window->position()); - deliver_mouse_event(*window, translated_event); - } - - hovered_window = nullptr; - - if (process_ongoing_window_move(event, hovered_window)) - return; - - if (process_ongoing_window_resize(event, hovered_window)) - return; - - if (m_cursor_tracking_button) - return m_cursor_tracking_button->on_mouse_event(event.translated(-m_cursor_tracking_button->screen_rect().location())); - - // This is quite hackish, but it's how the Button hover effect is implemented. - if (m_hovered_button && event.type() == Event::MouseMove) - m_hovered_button->on_mouse_event(event.translated(-m_hovered_button->screen_rect().location())); - - // FIXME: Now that the menubar has a dedicated window, is this special-casing really necessary? - if (MenuManager::the().has_open_menu() || menubar_rect().contains(event.position())) { - for_each_visible_window_of_type_from_front_to_back(WindowType::MenuApplet, [&](auto& window) { - if (!window.rect_in_menubar().contains(event.position())) - return IterationDecision::Continue; - hovered_window = &window; - return IterationDecision::Break; - }); - clear_resize_candidate(); - MenuManager::the().dispatch_event(event); - return; - } - - Window* event_window_with_frame = nullptr; - - if (m_active_input_tracking_window) { - // At this point, we have delivered the start of an input sequence to a - // client application. We must keep delivering to that client - // application until the input sequence is done. - // - // This prevents e.g. moving on one window out of the bounds starting - // a move in that other unrelated window, and other silly shenanigans. - if (!windows_who_received_mouse_event_due_to_cursor_tracking.contains(m_active_input_tracking_window)) { - auto translated_event = event.translated(-m_active_input_tracking_window->position()); - deliver_mouse_event(*m_active_input_tracking_window, translated_event); - windows_who_received_mouse_event_due_to_cursor_tracking.set(m_active_input_tracking_window.ptr()); - } - if (event.type() == Event::MouseUp && event.buttons() == 0) { - m_active_input_tracking_window = nullptr; - } - - for_each_visible_window_from_front_to_back([&](auto& window) { - if (window.frame().rect().contains(event.position())) { - hovered_window = &window; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - } else { - auto process_mouse_event_for_window = [&](Window& window) { - if (&window != m_resize_candidate.ptr()) - clear_resize_candidate(); - - // First check if we should initiate a move or resize (Logo+LMB or Logo+RMB). - // In those cases, the event is swallowed by the window manager. - if (window.is_movable()) { - if (!window.is_fullscreen() && m_keyboard_modifiers == Mod_Logo && event.type() == Event::MouseDown && event.button() == MouseButton::Left) { - hovered_window = &window; - start_window_move(window, event); - return; - } - if (window.is_resizable() && m_keyboard_modifiers == Mod_Logo && event.type() == Event::MouseDown && event.button() == MouseButton::Right && !window.blocking_modal_window()) { - hovered_window = &window; - start_window_resize(window, event); - return; - } - } - - if (m_keyboard_modifiers == Mod_Logo && event.type() == Event::MouseWheel) { - float opacity_change = -event.wheel_delta() * 0.05f; - float new_opacity = window.opacity() + opacity_change; - if (new_opacity < 0.05f) - new_opacity = 0.05f; - if (new_opacity > 1.0f) - new_opacity = 1.0f; - window.set_opacity(new_opacity); - return; - } - - ASSERT(window.frame().rect().contains(event.position())); - if (event.type() == Event::MouseDown) { - // We're clicking on something that's blocked by a modal window. - // Flash the modal window to let the user know about it. - if (auto* blocking_modal_window = window.blocking_modal_window()) - blocking_modal_window->frame().start_flash_animation(); - - if (window.type() == WindowType::Normal) - move_to_front_and_make_active(window); - else if (window.type() == WindowType::Desktop) - set_active_window(&window); - } - - // Well okay, let's see if we're hitting the frame or the window inside the frame. - if (window.rect().contains(event.position())) { - hovered_window = &window; - if (!window.global_cursor_tracking() && !windows_who_received_mouse_event_due_to_cursor_tracking.contains(&window) && !window.blocking_modal_window()) { - auto translated_event = event.translated(-window.position()); - deliver_mouse_event(window, translated_event); - if (event.type() == Event::MouseDown) { - m_active_input_tracking_window = window; - } - } - return; - } - - // We are hitting the frame, pass the event along to WindowFrame. - window.frame().on_mouse_event(event.translated(-window.frame().rect().location())); - event_window_with_frame = &window; - }; - - if (auto* fullscreen_window = active_fullscreen_window()) { - process_mouse_event_for_window(*fullscreen_window); - } else { - for_each_visible_window_from_front_to_back([&](Window& window) { - auto window_frame_rect = window.frame().rect(); - if (!window_frame_rect.contains(event.position())) - return IterationDecision::Continue; - process_mouse_event_for_window(window); - return IterationDecision::Break; - }); - } - - // Clicked outside of any window - if (!hovered_window && !event_window_with_frame && event.type() == Event::MouseDown) - set_active_window(nullptr); - } - - if (event_window_with_frame != m_resize_candidate.ptr()) - clear_resize_candidate(); -} - -void WindowManager::clear_resize_candidate() -{ - if (m_resize_candidate) - Compositor::the().invalidate_cursor(); - m_resize_candidate = nullptr; -} - -Gfx::IntRect WindowManager::menubar_rect() const -{ - if (active_fullscreen_window()) - return {}; - return MenuManager::the().menubar_rect(); -} - -Gfx::IntRect WindowManager::desktop_rect() const -{ - if (active_fullscreen_window()) - return {}; - return { - 0, - menubar_rect().bottom() + 1, - Screen::the().width(), - Screen::the().height() - menubar_rect().height() - 28 - }; -} - -void WindowManager::event(Core::Event& event) -{ - if (static_cast<Event&>(event).is_mouse_event()) { - Window* hovered_window = nullptr; - process_mouse_event(static_cast<MouseEvent&>(event), hovered_window); - set_hovered_window(hovered_window); - return; - } - - if (static_cast<Event&>(event).is_key_event()) { - auto& key_event = static_cast<const KeyEvent&>(event); - m_keyboard_modifiers = key_event.modifiers(); - - // Escape key cancels an ongoing drag. - if (key_event.type() == Event::KeyDown && key_event.key() == Key_Escape && m_dnd_client) { - // Notify the drag-n-drop client that the drag was cancelled. - m_dnd_client->post_message(Messages::WindowClient::DragCancelled()); - - // Also notify the currently hovered window (if any) that the ongoing drag was cancelled. - if (m_hovered_window && m_hovered_window->client() && m_hovered_window->client() != m_dnd_client) - m_hovered_window->client()->post_message(Messages::WindowClient::DragCancelled()); - - end_dnd_drag(); - return; - } - - if (MenuManager::the().current_menu()) { - MenuManager::the().dispatch_event(event); - return; - } - - if (key_event.type() == Event::KeyDown && ((key_event.modifiers() == Mod_Logo && key_event.key() == Key_Tab) || (key_event.modifiers() == (Mod_Logo | Mod_Shift) && key_event.key() == Key_Tab))) - m_switcher.show(); - if (m_switcher.is_visible()) { - m_switcher.on_key_event(key_event); - return; - } - - if (m_active_input_window) { - if (key_event.type() == Event::KeyDown && key_event.modifiers() == Mod_Logo) { - if (key_event.key() == Key_Down) { - if (m_active_input_window->is_resizable() && m_active_input_window->is_maximized()) { - maximize_windows(*m_active_input_window, false); - return; - } - if (m_active_input_window->is_minimizable()) - minimize_windows(*m_active_input_window, true); - return; - } - if (m_active_input_window->is_resizable()) { - if (key_event.key() == Key_Up) { - maximize_windows(*m_active_input_window, !m_active_input_window->is_maximized()); - return; - } - if (key_event.key() == Key_Left) { - if (m_active_input_window->tiled() != WindowTileType::None) { - m_active_input_window->set_tiled(WindowTileType::None); - return; - } - if (m_active_input_window->is_maximized()) - maximize_windows(*m_active_input_window, false); - m_active_input_window->set_tiled(WindowTileType::Left); - return; - } - if (key_event.key() == Key_Right) { - if (m_active_input_window->tiled() != WindowTileType::None) { - m_active_input_window->set_tiled(WindowTileType::None); - return; - } - if (m_active_input_window->is_maximized()) - maximize_windows(*m_active_input_window, false); - m_active_input_window->set_tiled(WindowTileType::Right); - return; - } - } - } - m_active_input_window->dispatch_event(event); - return; - } - } - - Core::Object::event(event); -} - -void WindowManager::set_highlight_window(Window* window) -{ - if (window == m_highlight_window) - return; - if (auto* previous_highlight_window = m_highlight_window.ptr()) - previous_highlight_window->invalidate(); - m_highlight_window = window; - if (m_highlight_window) - m_highlight_window->invalidate(); -} - -bool WindowManager::is_active_window_or_accessory(Window& window) const -{ - if (m_active_window == &window) - return true; - - if (!window.is_accessory()) - return false; - - return m_active_window == window.parent_window(); -} - -static bool window_type_can_become_active(WindowType type) -{ - return type == WindowType::Normal || type == WindowType::Desktop; -} - -void WindowManager::restore_active_input_window(Window* window) -{ - // If the previous active input window is gone, fall back to the - // current active window - if (!window) - window = active_window(); - // If the current active window is also gone, pick some other window - if (!window && pick_new_active_window(nullptr)) - return; - - set_active_input_window(window); -} - -Window* WindowManager::set_active_input_window(Window* window) -{ - if (window == m_active_input_window) - return window; - - Window* previous_input_window = m_active_input_window; - if (previous_input_window) - Core::EventLoop::current().post_event(*previous_input_window, make<Event>(Event::WindowInputLeft)); - - if (window) { - m_active_input_window = *window; - Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowInputEntered)); - } else { - m_active_input_window = nullptr; - } - - return previous_input_window; -} - -void WindowManager::set_active_window(Window* window, bool make_input) -{ - if (window) { - if (auto* modal_window = window->blocking_modal_window()) { - ASSERT(modal_window->is_modal()); - ASSERT(modal_window != window); - window = modal_window; - make_input = true; - } - - if (!window_type_can_become_active(window->type())) - return; - } - - auto* new_active_input_window = window; - if (window && window->is_accessory()) { - // The parent of an accessory window is always the active - // window, but input is routed to the accessory window - window = window->parent_window(); - } - - if (make_input) - set_active_input_window(new_active_input_window); - - if (window == m_active_window) - return; - - auto* previously_active_window = m_active_window.ptr(); - - ClientConnection* previously_active_client = nullptr; - ClientConnection* active_client = nullptr; - - if (previously_active_window) { - previously_active_client = previously_active_window->client(); - Core::EventLoop::current().post_event(*previously_active_window, make<Event>(Event::WindowDeactivated)); - previously_active_window->invalidate(); - m_active_window = nullptr; - m_active_input_tracking_window = nullptr; - tell_wm_listeners_window_state_changed(*previously_active_window); - } - - if (window) { - m_active_window = *window; - active_client = m_active_window->client(); - Core::EventLoop::current().post_event(*m_active_window, make<Event>(Event::WindowActivated)); - m_active_window->invalidate(); - if (auto* client = window->client()) { - MenuManager::the().set_current_menubar(client->app_menubar()); - } else { - MenuManager::the().set_current_menubar(nullptr); - } - tell_wm_listeners_window_state_changed(*m_active_window); - } else { - MenuManager::the().set_current_menubar(nullptr); - } - - if (active_client != previously_active_client) { - if (previously_active_client) - previously_active_client->deboost(); - if (active_client) - active_client->boost(); - } -} - -void WindowManager::set_hovered_window(Window* window) -{ - if (m_hovered_window == window) - return; - - if (m_hovered_window) - Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowLeft)); - - m_hovered_window = window; - - if (m_hovered_window) - Core::EventLoop::current().post_event(*m_hovered_window, make<Event>(Event::WindowEntered)); -} - -const ClientConnection* WindowManager::active_client() const -{ - if (m_active_window) - return m_active_window->client(); - return nullptr; -} - -void WindowManager::notify_client_changed_app_menubar(ClientConnection& client) -{ - if (active_client() == &client) - MenuManager::the().set_current_menubar(client.app_menubar()); -} - -const Cursor& WindowManager::active_cursor() const -{ - if (m_dnd_client) - return *m_drag_cursor; - - if (m_move_window) - return *m_move_cursor; - - if (m_resize_window || m_resize_candidate) { - switch (m_resize_direction) { - case ResizeDirection::Up: - case ResizeDirection::Down: - return *m_resize_vertically_cursor; - case ResizeDirection::Left: - case ResizeDirection::Right: - return *m_resize_horizontally_cursor; - case ResizeDirection::UpLeft: - case ResizeDirection::DownRight: - return *m_resize_diagonally_tlbr_cursor; - case ResizeDirection::UpRight: - case ResizeDirection::DownLeft: - return *m_resize_diagonally_bltr_cursor; - case ResizeDirection::None: - break; - } - } - - if (m_hovered_window) { - if (auto* modal_window = const_cast<Window&>(*m_hovered_window).blocking_modal_window()) { - if (modal_window->cursor()) - return *modal_window->cursor(); - } else if (m_hovered_window->cursor()) { - return *m_hovered_window->cursor(); - } - } - - return *m_arrow_cursor; -} - -void WindowManager::set_hovered_button(Button* button) -{ - m_hovered_button = button; -} - -void WindowManager::set_resize_candidate(Window& window, ResizeDirection direction) -{ - m_resize_candidate = window; - m_resize_direction = direction; -} - -ResizeDirection WindowManager::resize_direction_of_window(const Window& window) -{ - if (&window != m_resize_window) - return ResizeDirection::None; - return m_resize_direction; -} - -Gfx::IntRect WindowManager::maximized_window_rect(const Window& window) const -{ - Gfx::IntRect rect = Screen::the().rect(); - - // Subtract window title bar (leaving the border) - rect.set_y(rect.y() + window.frame().title_bar_rect().height()); - rect.set_height(rect.height() - window.frame().title_bar_rect().height()); - - // Subtract menu bar - rect.set_y(rect.y() + menubar_rect().height()); - rect.set_height(rect.height() - menubar_rect().height()); - - // Subtract taskbar window height if present - const_cast<WindowManager*>(this)->for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, [&rect](Window& taskbar_window) { - rect.set_height(rect.height() - taskbar_window.height()); - return IterationDecision::Break; - }); - - constexpr int tasteful_space_above_maximized_window = 2; - rect.set_y(rect.y() + tasteful_space_above_maximized_window); - rect.set_height(rect.height() - tasteful_space_above_maximized_window); - - return rect; -} - -void WindowManager::start_dnd_drag(ClientConnection& client, const String& text, Gfx::Bitmap* bitmap, const Core::MimeData& mime_data) -{ - ASSERT(!m_dnd_client); - m_dnd_client = client; - m_dnd_text = text; - m_dnd_bitmap = bitmap; - m_dnd_mime_data = mime_data; - Compositor::the().invalidate_cursor(); - m_active_input_tracking_window = nullptr; -} - -void WindowManager::end_dnd_drag() -{ - ASSERT(m_dnd_client); - Compositor::the().invalidate_cursor(); - m_dnd_client = nullptr; - m_dnd_text = {}; - m_dnd_bitmap = nullptr; -} - -Gfx::IntRect WindowManager::dnd_rect() const -{ - int bitmap_width = m_dnd_bitmap ? m_dnd_bitmap->width() : 0; - int bitmap_height = m_dnd_bitmap ? m_dnd_bitmap->height() : 0; - int width = font().width(m_dnd_text) + bitmap_width; - int height = max((int)font().glyph_height(), bitmap_height); - auto location = Compositor::the().current_cursor_rect().center().translated(8, 8); - return Gfx::IntRect(location, { width, height }).inflated(16, 8); -} - -bool WindowManager::update_theme(String theme_path, String theme_name) -{ - auto new_theme = Gfx::load_system_theme(theme_path); - if (!new_theme) - return false; - ASSERT(new_theme); - Gfx::set_system_theme(*new_theme); - m_palette = Gfx::PaletteImpl::create_with_shared_buffer(*new_theme); - Compositor::the().set_background_color(palette().desktop_background().to_string()); - HashTable<ClientConnection*> notified_clients; - for_each_window([&](Window& window) { - if (window.client()) { - if (!notified_clients.contains(window.client())) { - window.client()->post_message(Messages::WindowClient::UpdateSystemTheme(Gfx::current_system_theme_buffer_id())); - notified_clients.set(window.client()); - } - } - window.frame().layout_buttons(); - window.frame().set_button_icons(); - return IterationDecision::Continue; - }); - MenuManager::the().did_change_theme(); - auto wm_config = Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini"); - wm_config->write_entry("Theme", "Name", theme_name); - wm_config->sync(); - Compositor::the().invalidate_screen(); - return true; -} - -void WindowManager::did_popup_a_menu(Badge<Menu>) -{ - // Clear any ongoing input gesture - if (!m_active_input_tracking_window) - return; - m_active_input_tracking_window->set_automatic_cursor_tracking_enabled(false); - m_active_input_tracking_window = nullptr; -} - -void WindowManager::minimize_windows(Window& window, bool minimized) -{ - for_each_window_in_modal_stack(window, [&](auto& w, bool) { - w.set_minimized(minimized); - return IterationDecision::Continue; - }); -} - -void WindowManager::maximize_windows(Window& window, bool maximized) -{ - for_each_window_in_modal_stack(window, [&](auto& w, bool stack_top) { - if (stack_top) - w.set_maximized(maximized); - if (w.is_minimized()) - w.set_minimized(false); - return IterationDecision::Continue; - }); -} - -Gfx::IntPoint WindowManager::get_recommended_window_position(const Gfx::IntPoint& desired) -{ - // FIXME: Find a better source for the width and height to shift by. - Gfx::IntPoint shift(8, Gfx::WindowTheme::current().title_bar_height(palette()) + 10); - - // FIXME: Find a better source for this. - int taskbar_height = 28; - int menubar_height = MenuManager::the().menubar_rect().height(); - - const Window* overlap_window = nullptr; - for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, [&](Window& window) { - if (window.default_positioned() && (!overlap_window || overlap_window->window_id() < window.window_id())) { - overlap_window = &window; - } - return IterationDecision::Continue; - }); - - Gfx::IntPoint point; - if (overlap_window) { - point = overlap_window->position() + shift; - point = { point.x() % Screen::the().width(), - (point.y() >= (Screen::the().height() - taskbar_height)) - ? menubar_height + Gfx::WindowTheme::current().title_bar_height(palette()) - : point.y() }; - } else { - point = desired; - } - - return point; -} -} diff --git a/Services/WindowServer/WindowManager.h b/Services/WindowServer/WindowManager.h deleted file mode 100644 index 94e1f212d7..0000000000 --- a/Services/WindowServer/WindowManager.h +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/HashMap.h> -#include <AK/HashTable.h> -#include <AK/InlineLinkedList.h> -#include <AK/WeakPtr.h> -#include <LibCore/ConfigFile.h> -#include <LibCore/ElapsedTimer.h> -#include <LibGfx/Color.h> -#include <LibGfx/DisjointRectSet.h> -#include <LibGfx/Painter.h> -#include <LibGfx/Palette.h> -#include <LibGfx/Rect.h> -#include <WindowServer/Cursor.h> -#include <WindowServer/Event.h> -#include <WindowServer/MenuBar.h> -#include <WindowServer/MenuManager.h> -#include <WindowServer/Window.h> -#include <WindowServer/WindowSwitcher.h> -#include <WindowServer/WindowType.h> - -namespace WindowServer { - -class Screen; -class MouseEvent; -class Window; -class ClientConnection; -class WindowSwitcher; -class Button; - -enum class ResizeDirection { - None, - Left, - UpLeft, - Up, - UpRight, - Right, - DownRight, - Down, - DownLeft -}; - -class WindowManager : public Core::Object { - C_OBJECT(WindowManager) - - friend class Compositor; - friend class WindowFrame; - friend class WindowSwitcher; - -public: - static WindowManager& the(); - - explicit WindowManager(const Gfx::PaletteImpl&); - virtual ~WindowManager() override; - - Palette palette() const { return Palette(*m_palette); } - - RefPtr<Core::ConfigFile> config() const { return m_config; } - void reload_config(bool); - - void add_window(Window&); - void remove_window(Window&); - - void notify_title_changed(Window&); - void notify_modal_unparented(Window&); - void notify_rect_changed(Window&, const Gfx::IntRect& oldRect, const Gfx::IntRect& newRect); - void notify_minimization_state_changed(Window&); - void notify_opacity_changed(Window&); - void notify_occlusion_state_changed(Window&); - void notify_progress_changed(Window&); - void notify_client_changed_app_menubar(ClientConnection&); - - Gfx::IntRect maximized_window_rect(const Window&) const; - - const ClientConnection* dnd_client() const { return m_dnd_client.ptr(); } - const String& dnd_text() const { return m_dnd_text; } - const Core::MimeData& dnd_mime_data() const { return *m_dnd_mime_data; } - const Gfx::Bitmap* dnd_bitmap() const { return m_dnd_bitmap; } - Gfx::IntRect dnd_rect() const; - - void start_dnd_drag(ClientConnection&, const String& text, Gfx::Bitmap*, const Core::MimeData&); - void end_dnd_drag(); - - Window* active_window() { return m_active_window.ptr(); } - const Window* active_window() const { return m_active_window.ptr(); } - Window* active_input_window() { return m_active_input_window.ptr(); } - const Window* active_input_window() const { return m_active_input_window.ptr(); } - const ClientConnection* active_client() const; - - const Window* highlight_window() const { return m_highlight_window.ptr(); } - void set_highlight_window(Window*); - - void move_to_front_and_make_active(Window&); - - Gfx::IntRect menubar_rect() const; - Gfx::IntRect desktop_rect() const; - - const Cursor& active_cursor() const; - const Cursor& hidden_cursor() const { return *m_hidden_cursor; } - const Cursor& arrow_cursor() const { return *m_arrow_cursor; } - const Cursor& crosshair_cursor() const { return *m_crosshair_cursor; } - const Cursor& hand_cursor() const { return *m_hand_cursor; } - const Cursor& help_cursor() const { return *m_help_cursor; } - const Cursor& resize_horizontally_cursor() const { return *m_resize_horizontally_cursor; } - const Cursor& resize_vertically_cursor() const { return *m_resize_vertically_cursor; } - const Cursor& resize_diagonally_tlbr_cursor() const { return *m_resize_diagonally_tlbr_cursor; } - const Cursor& resize_diagonally_bltr_cursor() const { return *m_resize_diagonally_bltr_cursor; } - const Cursor& resize_column_cursor() const { return *m_resize_column_cursor; } - const Cursor& resize_row_cursor() const { return *m_resize_row_cursor; } - const Cursor& i_beam_cursor() const { return *m_i_beam_cursor; } - const Cursor& disallowed_cursor() const { return *m_disallowed_cursor; } - const Cursor& move_cursor() const { return *m_move_cursor; } - const Cursor& drag_cursor() const { return *m_drag_cursor; } - const Cursor& wait_cursor() const { return *m_wait_cursor; } - - const Gfx::Font& font() const; - const Gfx::Font& window_title_font() const; - - bool set_resolution(int width, int height); - Gfx::IntSize resolution() const; - - void set_acceleration_factor(double); - void set_scroll_step_size(unsigned); - - Window* set_active_input_window(Window*); - void restore_active_input_window(Window*); - void set_active_window(Window*, bool make_input = true); - void set_hovered_button(Button*); - - const Button* cursor_tracking_button() const { return m_cursor_tracking_button.ptr(); } - void set_cursor_tracking_button(Button*); - - void set_resize_candidate(Window&, ResizeDirection); - void clear_resize_candidate(); - ResizeDirection resize_direction_of_window(const Window&); - - void tell_wm_listeners_window_state_changed(Window&); - void tell_wm_listeners_window_icon_changed(Window&); - void tell_wm_listeners_window_rect_changed(Window&); - - bool is_active_window_or_accessory(Window&) const; - - void start_window_resize(Window&, const Gfx::IntPoint&, MouseButton); - void start_window_resize(Window&, const MouseEvent&); - - const Window* active_fullscreen_window() const - { - if (m_active_window && m_active_window->is_fullscreen()) { - return m_active_window; - } - return nullptr; - }; - - Window* active_fullscreen_window() - { - if (m_active_window && m_active_window->is_fullscreen()) { - return m_active_window; - } - return nullptr; - } - - bool update_theme(String theme_path, String theme_name); - - void set_hovered_window(Window*); - void deliver_mouse_event(Window& window, MouseEvent& event); - - void did_popup_a_menu(Badge<Menu>); - - void start_menu_doubleclick(Window& window, const MouseEvent& event); - bool is_menu_doubleclick(Window& window, const MouseEvent& event) const; - - void minimize_windows(Window&, bool); - void maximize_windows(Window&, bool); - - template<typename Function> - IterationDecision for_each_window_in_modal_stack(Window& window, Function f) - { - auto* blocking_modal_window = window.blocking_modal_window(); - if (blocking_modal_window || window.is_modal()) { - Vector<Window*> modal_stack; - auto* modal_stack_top = blocking_modal_window ? blocking_modal_window : &window; - for (auto* parent = modal_stack_top->parent_window(); parent; parent = parent->parent_window()) { - auto* blocked_by = parent->blocking_modal_window(); - if (!blocked_by || (blocked_by != modal_stack_top && !modal_stack_top->is_descendant_of(*blocked_by))) - break; - modal_stack.append(parent); - if (!parent->is_modal()) - break; - } - if (!modal_stack.is_empty()) { - for (size_t i = modal_stack.size(); i > 0; i--) { - IterationDecision decision = f(*modal_stack[i - 1], false); - if (decision != IterationDecision::Continue) - return decision; - } - } - return f(*modal_stack_top, true); - } else { - // Not a modal window stack, just "iterate" over this window - return f(window, true); - } - } - - Gfx::IntPoint get_recommended_window_position(const Gfx::IntPoint& desired); - -private: - NonnullRefPtr<Cursor> get_cursor(const String& name); - - void process_mouse_event(MouseEvent&, Window*& hovered_window); - void process_event_for_doubleclick(Window& window, MouseEvent& event); - bool process_ongoing_window_resize(const MouseEvent&, Window*& hovered_window); - bool process_ongoing_window_move(MouseEvent&, Window*& hovered_window); - bool process_ongoing_drag(MouseEvent&, Window*& hovered_window); - void start_window_move(Window&, const MouseEvent&); - template<typename Callback> - IterationDecision for_each_visible_window_of_type_from_back_to_front(WindowType, Callback, bool ignore_highlight = false); - template<typename Callback> - IterationDecision for_each_visible_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); - template<typename Callback> - IterationDecision for_each_visible_window_from_front_to_back(Callback); - template<typename Callback> - IterationDecision for_each_visible_window_from_back_to_front(Callback); - template<typename Callback> - void for_each_window_listening_to_wm_events(Callback); - template<typename Callback> - void for_each_window(Callback); - template<typename Callback> - IterationDecision for_each_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); - - virtual void event(Core::Event&) override; - void paint_window_frame(const Window&); - void tell_wm_listener_about_window(Window& listener, Window&); - void tell_wm_listener_about_window_icon(Window& listener, Window&); - void tell_wm_listener_about_window_rect(Window& listener, Window&); - bool pick_new_active_window(Window*); - - void do_move_to_front(Window&, bool, bool); - - RefPtr<Cursor> m_hidden_cursor; - RefPtr<Cursor> m_arrow_cursor; - RefPtr<Cursor> m_hand_cursor; - RefPtr<Cursor> m_help_cursor; - RefPtr<Cursor> m_resize_horizontally_cursor; - RefPtr<Cursor> m_resize_vertically_cursor; - RefPtr<Cursor> m_resize_diagonally_tlbr_cursor; - RefPtr<Cursor> m_resize_diagonally_bltr_cursor; - RefPtr<Cursor> m_resize_column_cursor; - RefPtr<Cursor> m_resize_row_cursor; - RefPtr<Cursor> m_i_beam_cursor; - RefPtr<Cursor> m_disallowed_cursor; - RefPtr<Cursor> m_move_cursor; - RefPtr<Cursor> m_drag_cursor; - RefPtr<Cursor> m_wait_cursor; - RefPtr<Cursor> m_crosshair_cursor; - - InlineLinkedList<Window> m_windows_in_order; - - struct DoubleClickInfo { - struct ClickMetadata { - Core::ElapsedTimer clock; - Gfx::IntPoint last_position; - }; - - const ClickMetadata& metadata_for_button(MouseButton) const; - ClickMetadata& metadata_for_button(MouseButton); - - void reset() - { - m_left = {}; - m_right = {}; - m_middle = {}; - m_back = {}; - m_forward = {}; - } - - WeakPtr<Window> m_clicked_window; - - private: - ClickMetadata m_left; - ClickMetadata m_right; - ClickMetadata m_middle; - ClickMetadata m_back; - ClickMetadata m_forward; - }; - - bool is_considered_doubleclick(const MouseEvent& event, const DoubleClickInfo::ClickMetadata& metadata) const; - - DoubleClickInfo m_double_click_info; - int m_double_click_speed { 0 }; - int m_max_distance_for_double_click { 4 }; - - WeakPtr<Window> m_active_window; - WeakPtr<Window> m_hovered_window; - WeakPtr<Window> m_highlight_window; - WeakPtr<Window> m_active_input_window; - WeakPtr<Window> m_active_input_tracking_window; - - WeakPtr<Window> m_move_window; - Gfx::IntPoint m_move_origin; - Gfx::IntPoint m_move_window_origin; - - WeakPtr<Window> m_resize_window; - WeakPtr<Window> m_resize_candidate; - MouseButton m_resizing_mouse_button { MouseButton::None }; - Gfx::IntRect m_resize_window_original_rect; - Gfx::IntPoint m_resize_origin; - ResizeDirection m_resize_direction { ResizeDirection::None }; - - u8 m_keyboard_modifiers { 0 }; - - WindowSwitcher m_switcher; - - WeakPtr<Button> m_cursor_tracking_button; - WeakPtr<Button> m_hovered_button; - - NonnullRefPtr<Gfx::PaletteImpl> m_palette; - - RefPtr<Core::ConfigFile> m_config; - - WeakPtr<ClientConnection> m_dnd_client; - String m_dnd_text; - RefPtr<Core::MimeData> m_dnd_mime_data; - RefPtr<Gfx::Bitmap> m_dnd_bitmap; -}; - -template<typename Callback> -IterationDecision WindowManager::for_each_visible_window_of_type_from_back_to_front(WindowType type, Callback callback, bool ignore_highlight) -{ - bool do_highlight_window_at_end = false; - for (auto& window : m_windows_in_order) { - if (!window.is_visible()) - continue; - if (window.is_minimized()) - continue; - if (window.type() != type) - continue; - if (!ignore_highlight && m_highlight_window == &window) { - do_highlight_window_at_end = true; - continue; - } - if (callback(window) == IterationDecision::Break) - return IterationDecision::Break; - } - if (do_highlight_window_at_end) { - if (callback(*m_highlight_window) == IterationDecision::Break) - return IterationDecision::Break; - } - return IterationDecision::Continue; -} - -template<typename Callback> -IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Callback callback) -{ - if (for_each_visible_window_of_type_from_back_to_front(WindowType::Desktop, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_back_to_front(WindowType::Normal, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_back_to_front(WindowType::Notification, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_back_to_front(WindowType::Tooltip, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menubar, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menu, callback) == IterationDecision::Break) - return IterationDecision::Break; - return for_each_visible_window_of_type_from_back_to_front(WindowType::WindowSwitcher, callback); -} - -template<typename Callback> -IterationDecision WindowManager::for_each_visible_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight) -{ - if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) { - if (callback(*m_highlight_window) == IterationDecision::Break) - return IterationDecision::Break; - } - - for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { - if (!window->is_visible()) - continue; - if (window->is_minimized()) - continue; - if (window->type() != type) - continue; - if (!ignore_highlight && window == m_highlight_window) - continue; - if (callback(*window) == IterationDecision::Break) - return IterationDecision::Break; - } - return IterationDecision::Continue; -} - -template<typename Callback> -IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Callback callback) -{ - if (for_each_visible_window_of_type_from_front_to_back(WindowType::WindowSwitcher, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menu, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menubar, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_front_to_back(WindowType::Tooltip, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_front_to_back(WindowType::Notification, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_front_to_back(WindowType::Taskbar, callback) == IterationDecision::Break) - return IterationDecision::Break; - if (for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, callback) == IterationDecision::Break) - return IterationDecision::Break; - return for_each_visible_window_of_type_from_front_to_back(WindowType::Desktop, callback); -} - -template<typename Callback> -void WindowManager::for_each_window_listening_to_wm_events(Callback callback) -{ - for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { - if (!window->listens_to_wm_events()) - continue; - if (callback(*window) == IterationDecision::Break) - return; - } -} - -template<typename Callback> -void WindowManager::for_each_window(Callback callback) -{ - for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { - if (callback(*window) == IterationDecision::Break) - return; - } -} - -template<typename Callback> -IterationDecision WindowManager::for_each_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight) -{ - if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) { - if (callback(*m_highlight_window) == IterationDecision::Break) - return IterationDecision::Break; - } - - for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { - if (window->type() != type) - continue; - if (!ignore_highlight && window == m_highlight_window) - continue; - if (callback(*window) == IterationDecision::Break) - return IterationDecision::Break; - } - return IterationDecision::Continue; -} - -} diff --git a/Services/WindowServer/WindowServer.ipc b/Services/WindowServer/WindowServer.ipc deleted file mode 100644 index 72399de1db..0000000000 --- a/Services/WindowServer/WindowServer.ipc +++ /dev/null @@ -1,117 +0,0 @@ -endpoint WindowServer = 2 -{ - Greet() => (i32 client_id, Gfx::IntRect screen_rect, i32 system_theme_buffer_id) - - CreateMenubar() => (i32 menubar_id) - DestroyMenubar(i32 menubar_id) => () - - CreateMenu([UTF8] String menu_title) => (i32 menu_id) - DestroyMenu(i32 menu_id) => () - - AddMenuToMenubar(i32 menubar_id, i32 menu_id) => () - SetApplicationMenubar(i32 menubar_id) => () - - SetSystemMenu(i32 menu_id) => () - - AddMenuItem( - i32 menu_id, - i32 identifier, - i32 submenu_id, - [UTF8] String text, - bool enabled, - bool checkable, - bool checked, - bool is_default, - [UTF8] String shortcut, - i32 icon_buffer_id, - bool exclusive) => () - - AddMenuSeparator(i32 menu_id) => () - - UpdateMenuItem(i32 menu_id, i32 identifier, i32 submenu_id, [UTF8] String text, bool enabled, bool checkable, bool checked, bool is_default, [UTF8] String shortcut) => () - - CreateWindow( - Gfx::IntRect rect, - bool auto_position, - bool has_alpha_channel, - bool modal, - bool minimizable, - bool resizable, - bool fullscreen, - bool frameless, - bool accessory, - float opacity, - Gfx::IntSize base_size, - Gfx::IntSize size_increment, - Optional<Gfx::IntSize> resize_aspect_ratio, - i32 type, - [UTF8] String title, - i32 parent_window_id) => (i32 window_id) - - DestroyWindow(i32 window_id) => (Vector<i32> destroyed_window_ids) - - SetWindowTitle(i32 window_id, [UTF8] String title) => () - GetWindowTitle(i32 window_id) => ([UTF8] String title) - - SetWindowProgress(i32 window_id, i32 progress) =| - - SetWindowRect(i32 window_id, Gfx::IntRect rect) => (Gfx::IntRect rect) - GetWindowRect(i32 window_id) => (Gfx::IntRect rect) - - GetWindowRectInMenubar(i32 window_id) => (Gfx::IntRect rect) - - IsMaximized(i32 window_id) => (bool maximized) - - InvalidateRect(i32 window_id, Vector<Gfx::IntRect> rects, bool ignore_occlusion) =| - DidFinishPainting(i32 window_id, Vector<Gfx::IntRect> rects) =| - - SetGlobalCursorTracking(i32 window_id, bool enabled) => () - SetWindowOpacity(i32 window_id, float opacity) => () - - SetWindowBackingStore(i32 window_id, i32 bpp, i32 pitch, i32 shbuf_id, bool has_alpha_channel, Gfx::IntSize size, bool flush_immediately) => () - - WM_SetActiveWindow(i32 client_id, i32 window_id) =| - WM_SetWindowMinimized(i32 client_id, i32 window_id, bool minimized) =| - WM_StartWindowResize(i32 client_id, i32 window_id) =| - WM_PopupWindowMenu(i32 client_id, i32 window_id, Gfx::IntPoint screen_position) =| - WM_SetWindowTaskbarRect(i32 client_id, i32 window_id, Gfx::IntRect rect) =| - - SetWindowHasAlphaChannel(i32 window_id, bool has_alpha_channel) => () - MoveWindowToFront(i32 window_id) => () - SetFullscreen(i32 window_id, bool fullscreen) => () - PopupMenu(i32 menu_id, Gfx::IntPoint screen_position) => () - DismissMenu(i32 menu_id) => () - - AsyncSetWallpaper(String path) =| - - SetBackgroundColor(String background_color) => () - SetWallpaperMode(String mode) => () - - SetResolution(Gfx::IntSize resolution) => (bool success, Gfx::IntSize resolution) - SetWindowIconBitmap(i32 window_id, Gfx::ShareableBitmap icon) => () - - GetWallpaper() => (String path) - SetWindowCursor(i32 window_id, i32 cursor_type) => () - SetWindowCustomCursor(i32 window_id, Gfx::ShareableBitmap cursor) => () - - StartDrag([UTF8] String text, HashMap<String,ByteBuffer> mime_data, i32 bitmap_id, Gfx::IntSize bitmap_size) => (bool started) - - SetSystemTheme(String theme_path, [UTF8] String theme_name) => (bool success) - GetSystemTheme() => ([UTF8] String theme_name) - - SetWindowBaseSizeAndSizeIncrement(i32 window_id, Gfx::IntSize base_size, Gfx::IntSize size_increment) => () - SetWindowResizeAspectRatio(i32 window_id, Optional<Gfx::IntSize> resize_aspect_ratio) => () - - EnableDisplayLink() =| - DisableDisplayLink() =| - - GetGlobalCursorPosition() => (Gfx::IntPoint position) - - SetMouseAcceleration(float factor) => () - GetMouseAcceleration() => (float factor) - - SetScrollStepSize(u32 step_size) => () - GetScrollStepSize() => (u32 step_size) - - Pong() =| -} diff --git a/Services/WindowServer/WindowSwitcher.cpp b/Services/WindowServer/WindowSwitcher.cpp deleted file mode 100644 index 803887c657..0000000000 --- a/Services/WindowServer/WindowSwitcher.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <LibGfx/Bitmap.h> -#include <LibGfx/Font.h> -#include <LibGfx/StylePainter.h> -#include <WindowServer/Compositor.h> -#include <WindowServer/Event.h> -#include <WindowServer/Screen.h> -#include <WindowServer/WindowManager.h> -#include <WindowServer/WindowSwitcher.h> - -namespace WindowServer { - -static WindowSwitcher* s_the; - -WindowSwitcher& WindowSwitcher::the() -{ - ASSERT(s_the); - return *s_the; -} - -WindowSwitcher::WindowSwitcher() -{ - s_the = this; -} - -WindowSwitcher::~WindowSwitcher() -{ -} - -void WindowSwitcher::set_visible(bool visible) -{ - if (m_visible == visible) - return; - m_visible = visible; - Compositor::the().invalidate_occlusions(); - if (m_switcher_window) - m_switcher_window->set_visible(visible); - if (!m_visible) - return; - refresh(); -} - -Window* WindowSwitcher::selected_window() -{ - if (m_selected_index < 0 || m_selected_index >= static_cast<int>(m_windows.size())) - return nullptr; - return m_windows[m_selected_index].ptr(); -} - -void WindowSwitcher::event(Core::Event& event) -{ - if (!static_cast<Event&>(event).is_mouse_event()) - return; - - auto& mouse_event = static_cast<MouseEvent&>(event); - int new_hovered_index = -1; - for (size_t i = 0; i < m_windows.size(); ++i) { - auto item_rect = this->item_rect(i); - if (item_rect.contains(mouse_event.position())) { - new_hovered_index = i; - break; - } - } - - if (mouse_event.type() == Event::MouseMove) { - if (m_hovered_index != new_hovered_index) { - m_hovered_index = new_hovered_index; - redraw(); - } - } - - if (new_hovered_index == -1) - return; - - if (mouse_event.type() == Event::MouseDown) - select_window_at_index(new_hovered_index); - - event.accept(); -} - -void WindowSwitcher::on_key_event(const KeyEvent& event) -{ - if (event.type() == Event::KeyUp) { - if (event.key() == Key_Logo) { - if (auto* window = selected_window()) { - window->set_minimized(false); - WindowManager::the().move_to_front_and_make_active(*window); - } - WindowManager::the().set_highlight_window(nullptr); - hide(); - } - return; - } - - if (event.key() == Key_LeftShift || event.key() == Key_RightShift) - return; - if (event.key() != Key_Tab) { - WindowManager::the().set_highlight_window(nullptr); - hide(); - return; - } - ASSERT(!m_windows.is_empty()); - - int new_selected_index; - - if (!event.shift()) { - new_selected_index = (m_selected_index + 1) % static_cast<int>(m_windows.size()); - } else { - new_selected_index = (m_selected_index - 1) % static_cast<int>(m_windows.size()); - if (new_selected_index < 0) - new_selected_index = static_cast<int>(m_windows.size()) - 1; - } - ASSERT(new_selected_index < static_cast<int>(m_windows.size())); - - select_window_at_index(new_selected_index); -} - -void WindowSwitcher::select_window(Window& window) -{ - for (size_t i = 0; i < m_windows.size(); ++i) { - if (m_windows.at(i) == &window) { - select_window_at_index(i); - return; - } - } -} - -void WindowSwitcher::select_window_at_index(int index) -{ - m_selected_index = index; - auto* highlight_window = m_windows.at(index).ptr(); - ASSERT(highlight_window); - WindowManager::the().set_highlight_window(highlight_window); - redraw(); -} - -void WindowSwitcher::redraw() -{ - draw(); - Compositor::the().invalidate_screen(m_rect); -} - -Gfx::IntRect WindowSwitcher::item_rect(int index) const -{ - return { - padding(), - padding() + index * item_height(), - m_rect.width() - padding() * 2, - item_height() - }; -} - -void WindowSwitcher::draw() -{ - auto palette = WindowManager::the().palette(); - Gfx::Painter painter(*m_switcher_window->backing_store()); - painter.fill_rect({ {}, m_rect.size() }, palette.window()); - painter.draw_rect({ {}, m_rect.size() }, palette.threed_shadow2()); - for (size_t index = 0; index < m_windows.size(); ++index) { - auto& window = *m_windows.at(index); - auto item_rect = this->item_rect(index); - Color text_color; - Color rect_text_color; - if (static_cast<int>(index) == m_selected_index) { - painter.fill_rect(item_rect, palette.selection()); - text_color = palette.selection_text(); - rect_text_color = palette.threed_shadow1(); - } else { - if (static_cast<int>(index) == m_hovered_index) - Gfx::StylePainter::paint_button(painter, item_rect, palette, Gfx::ButtonStyle::CoolBar, false, true); - text_color = palette.window_text(); - rect_text_color = palette.threed_shadow2(); - } - item_rect.shrink(item_padding(), 0); - Gfx::IntRect thumbnail_rect = { item_rect.location().translated(0, 5), { thumbnail_width(), thumbnail_height() } }; - if (window.backing_store()) { - painter.draw_scaled_bitmap(thumbnail_rect, *window.backing_store(), window.backing_store()->rect()); - Gfx::StylePainter::paint_frame(painter, thumbnail_rect.inflated(4, 4), palette, Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2); - } - Gfx::IntRect icon_rect = { thumbnail_rect.bottom_right().translated(-window.icon().width(), -window.icon().height()), { window.icon().width(), window.icon().height() } }; - painter.fill_rect(icon_rect, palette.window()); - painter.blit(icon_rect.location(), window.icon(), window.icon().rect()); - painter.draw_text(item_rect.translated(thumbnail_width() + 12, 0), window.title(), WindowManager::the().window_title_font(), Gfx::TextAlignment::CenterLeft, text_color); - painter.draw_text(item_rect, window.rect().to_string(), Gfx::TextAlignment::CenterRight, rect_text_color); - } -} - -void WindowSwitcher::refresh() -{ - auto& wm = WindowManager::the(); - const Window* selected_window = nullptr; - if (m_selected_index > 0 && m_windows[m_selected_index]) - selected_window = m_windows[m_selected_index].ptr(); - if (!selected_window) - selected_window = wm.highlight_window() ? wm.highlight_window() : wm.active_window(); - m_windows.clear(); - m_selected_index = 0; - int window_count = 0; - int longest_title_width = 0; - wm.for_each_window_of_type_from_front_to_back( - WindowType::Normal, [&](Window& window) { - if (window.is_frameless()) - return IterationDecision::Continue; - ++window_count; - longest_title_width = max(longest_title_width, wm.font().width(window.title())); - if (selected_window == &window) - m_selected_index = m_windows.size(); - m_windows.append(window); - return IterationDecision::Continue; - }, - true); - if (m_windows.is_empty()) { - hide(); - return; - } - int space_for_window_rect = 180; - m_rect.set_width(thumbnail_width() + longest_title_width + space_for_window_rect + padding() * 2 + item_padding() * 2); - m_rect.set_height(window_count * item_height() + padding() * 2); - m_rect.center_within(Screen::the().rect()); - if (!m_switcher_window) - m_switcher_window = Window::construct(*this, WindowType::WindowSwitcher); - m_switcher_window->set_rect(m_rect); - redraw(); -} - -void WindowSwitcher::refresh_if_needed() -{ - if (m_visible) - refresh(); -} - -} diff --git a/Services/WindowServer/WindowSwitcher.h b/Services/WindowServer/WindowSwitcher.h deleted file mode 100644 index 8b52eea372..0000000000 --- a/Services/WindowServer/WindowSwitcher.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include <AK/Vector.h> -#include <AK/WeakPtr.h> -#include <LibCore/Object.h> -#include <LibGfx/Forward.h> -#include <LibGfx/Rect.h> - -namespace WindowServer { - -class KeyEvent; -class Window; - -class WindowSwitcher final : public Core::Object { - C_OBJECT(WindowSwitcher) -public: - static WindowSwitcher& the(); - - WindowSwitcher(); - virtual ~WindowSwitcher() override; - - bool is_visible() const { return m_visible; } - void set_visible(bool); - - void show() { set_visible(true); } - void hide() { set_visible(false); } - - void on_key_event(const KeyEvent&); - - void refresh(); - void refresh_if_needed(); - - void select_window(Window&); - -private: - int thumbnail_width() const { return 40; } - int thumbnail_height() const { return 40; } - int item_height() const { return 10 + thumbnail_height(); } - int padding() const { return 8; } - int item_padding() const { return 8; } - - void draw(); - void redraw(); - void select_window_at_index(int index); - Gfx::IntRect item_rect(int index) const; - Window* selected_window(); - - virtual void event(Core::Event&) override; - - RefPtr<Window> m_switcher_window; - Gfx::IntRect m_rect; - bool m_visible { false }; - Vector<WeakPtr<Window>> m_windows; - int m_selected_index { 0 }; - int m_hovered_index { -1 }; -}; - -} diff --git a/Services/WindowServer/WindowType.h b/Services/WindowServer/WindowType.h deleted file mode 100644 index a3c53007a1..0000000000 --- a/Services/WindowServer/WindowType.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -// Keep this in sync with GUI::WindowType. -enum class WindowType { - Invalid = 0, - Normal, - Menu, - WindowSwitcher, - Taskbar, - Tooltip, - Menubar, - MenuApplet, - Notification, - Desktop, -}; diff --git a/Services/WindowServer/main.cpp b/Services/WindowServer/main.cpp deleted file mode 100644 index ca8d997f0d..0000000000 --- a/Services/WindowServer/main.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "AppletManager.h" -#include "Compositor.h" -#include "EventLoop.h" -#include "Screen.h" -#include "WindowManager.h" -#include <AK/SharedBuffer.h> -#include <LibCore/ConfigFile.h> -#include <LibGfx/Palette.h> -#include <LibGfx/SystemTheme.h> -#include <signal.h> -#include <stdio.h> -#include <string.h> - -int main(int, char**) -{ - if (pledge("stdio video thread shared_buffer accept rpath wpath cpath unix proc fattr sigaction", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil /res"); - return 1; - } - - if (unveil("/tmp", "cw") < 0) { - perror("unveil /tmp cw"); - return 1; - } - - if (unveil("/etc/WindowServer/WindowServer.ini", "rwc") < 0) { - perror("unveil /etc/WindowServer/WindowServer.ini"); - return 1; - } - - if (unveil("/dev", "rw") < 0) { - perror("unveil /dev rw"); - return 1; - } - - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_flags = SA_NOCLDWAIT; - act.sa_handler = SIG_IGN; - int rc = sigaction(SIGCHLD, &act, nullptr); - if (rc < 0) { - perror("sigaction"); - return 1; - } - - auto wm_config = Core::ConfigFile::open("/etc/WindowServer/WindowServer.ini"); - auto theme_name = wm_config->read_entry("Theme", "Name", "Default"); - - auto theme = Gfx::load_system_theme(String::format("/res/themes/%s.ini", theme_name.characters())); - ASSERT(theme); - Gfx::set_system_theme(*theme); - auto palette = Gfx::PaletteImpl::create_with_shared_buffer(*theme); - - WindowServer::EventLoop loop; - - if (pledge("stdio video thread shared_buffer accept rpath wpath cpath proc", nullptr) < 0) { - perror("pledge"); - return 1; - } - - WindowServer::Screen screen(wm_config->read_num_entry("Screen", "Width", 1024), - wm_config->read_num_entry("Screen", "Height", 768)); - screen.set_acceleration_factor(atof(wm_config->read_entry("Mouse", "AccelerationFactor", "1.0").characters())); - screen.set_scroll_step_size(wm_config->read_num_entry("Mouse", "ScrollStepSize", 4)); - WindowServer::Compositor::the(); - auto wm = WindowServer::WindowManager::construct(*palette); - auto am = WindowServer::AppletManager::construct(); - auto mm = WindowServer::MenuManager::construct(); - - if (unveil("/tmp", "") < 0) { - perror("unveil /tmp"); - return 1; - } - - if (unveil("/dev", "") < 0) { - perror("unveil /dev"); - return 1; - } - - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - dbgln("Entering WindowServer main loop"); - loop.exec(); - ASSERT_NOT_REACHED(); -} |