Skip to content

Commit

Permalink
Start working on a simple Launcher app.
Browse files Browse the repository at this point in the history
Let GButton have an optional icon (GraphicsBitmap) that gets rendered in the
middle of the button if present.

Also add GraphicsBitmap::load_from_file() which allows mmap'ed RGBA32 files.
I wrote a little program to take "raw" files from GIMP and swizzle them into
the correct byte order.
  • Loading branch information
awesomekling committed Feb 7, 2019
1 parent 71b9ec1 commit 887b4a7
Show file tree
Hide file tree
Showing 29 changed files with 293 additions and 11 deletions.
Binary file added Base/res/icons/FontEditor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Base/res/icons/FontEditor.rgb
Binary file not shown.
Binary file added Base/res/icons/Terminal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Base/res/icons/Terminal.rgb
Binary file not shown.
Binary file added Base/res/icons/file.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Base/res/icons/file.rgb
Binary file not shown.
Binary file added Base/res/icons/folder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Base/res/icons/folder.rgb
Binary file not shown.
Binary file added Base/res/icons/generic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Base/res/icons/generic.rgb
Binary file not shown.
6 changes: 5 additions & 1 deletion Kernel/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
#include "BochsVGADevice.h"

//#define SPAWN_GUITEST
#define SPAWN_GUITEST2
#define SPAWN_LAUNCHER
//#define SPAWN_GUITEST2
#define SPAWN_CLOCK
//#define SPAWN_FONTEDITOR
//#define SPAWN_MULTIPLE_SHELLS
Expand Down Expand Up @@ -113,6 +114,9 @@ static void init_stage2()
#ifdef SPAWN_GUITEST2
Process::create_user_process("/bin/guitest2", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, move(environment), tty0);
#endif
#ifdef SPAWN_LAUNCHER
Process::create_user_process("/bin/Launcher", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, move(environment), tty0);
#endif
#ifdef SPAWN_CLOCK
Process::create_user_process("/bin/Clock", (uid_t)100, (gid_t)100, (pid_t)0, error, { }, move(environment), tty0);
#endif
Expand Down
2 changes: 2 additions & 0 deletions Kernel/makeall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ make -C ../FontEditor clean && \
make -C ../FontEditor && \
make -C ../Clock clean && \
make -C ../Clock && \
make -C ../Launcher clean && \
make -C ../Launcher && \
make clean &&\
make && \
sudo ./sync.sh
Expand Down
2 changes: 2 additions & 0 deletions Kernel/makeuserland.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ make -C ../Terminal clean && \
make -C ../Terminal && \
make -C ../Clock clean && \
make -C ../Clock && \
make -C ../Launcher clean && \
make -C ../Launcher && \
make -C ../Userland clean && \
make -C ../Userland && \
sudo ./sync.sh
Expand Down
1 change: 1 addition & 0 deletions Kernel/sync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ cp -v ../Userland/guitest2 mnt/bin/guitest2
cp -v ../Userland/sysctl mnt/bin/sysctl
cp -v ../Terminal/Terminal mnt/bin/Terminal
cp -v ../FontEditor/FontEditor mnt/bin/FontEditor
cp -v ../Launcher/Launcher mnt/bin/Launcher
cp -v ../Clock/Clock mnt/bin/Clock
ln -s FontEditor mnt/bin/ff
ln -s Clock mnt/bin/cl
Expand Down
3 changes: 3 additions & 0 deletions Launcher/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.o
*.d
FontEditor
Binary file added Launcher/Launcher
Binary file not shown.
34 changes: 34 additions & 0 deletions Launcher/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
OBJS = \
main.o

APP = Launcher

ARCH_FLAGS =
STANDARD_FLAGS = -std=c++17 -nostdinc++ -nostdlib -nostdinc
USERLAND_FLAGS = -ffreestanding -fno-stack-protector -fno-ident
WARNING_FLAGS = -Wextra -Wall -Wundef -Wcast-qual -Wwrite-strings
FLAVOR_FLAGS = -march=i386 -mregparm=3 -m32 -fno-exceptions -fno-rtti -fmerge-all-constants -fno-unroll-loops -fno-pie -fno-pic
OPTIMIZATION_FLAGS = -Oz -fno-asynchronous-unwind-tables
INCLUDE_FLAGS = -I.. -I. -I../LibC

DEFINES = -DSERENITY -DSANITIZE_PTRS -DUSERLAND

CXXFLAGS = -MMD -MP $(WARNING_FLAGS) $(OPTIMIZATION_FLAGS) $(USERLAND_FLAGS) $(FLAVOR_FLAGS) $(ARCH_FLAGS) $(STANDARD_FLAGS) $(INCLUDE_FLAGS) $(DEFINES)
CXX = clang
LD = ld
AR = ar
LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections

all: $(APP)

$(APP): $(OBJS)
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../LibGUI/LibGUI.a ../LibC/LibC.a

.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<

-include $(OBJS:%.o=%.d)

clean:
@echo "CLEAN"; rm -f $(APPS) $(OBJS) *.d

79 changes: 79 additions & 0 deletions Launcher/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include <SharedGraphics/GraphicsBitmap.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GEventLoop.h>
#include <signal.h>
#include <unistd.h>

static GWindow* make_launcher_window();

void handle_sigchld(int)
{
dbgprintf("Launcher(%d) Got SIGCHLD\n", getpid());
int pid = waitpid(-1, nullptr, 0);
dbgprintf("Launcher(%d) waitpid() returned %d\n", getpid(), pid);
ASSERT(pid > 0);
}

int main(int, char**)
{
signal(SIGCHLD, handle_sigchld);

GEventLoop loop;

auto* launcher_window = make_launcher_window();
launcher_window->set_should_exit_app_on_close(true);
launcher_window->show();

return loop.exec();
}

GWindow* make_launcher_window()
{
auto* window = new GWindow;
window->set_title("Launcher");
window->set_rect({ 50, 50, 300, 60 });

auto* widget = new GWidget;
window->set_main_widget(widget);
widget->set_relative_rect({ 0, 0, 300, 60 });

auto* terminal_button = new GButton(widget);
terminal_button->set_relative_rect({ 5, 5, 50, 50 });
terminal_button->set_icon(GraphicsBitmap::load_from_file("/res/icons/Terminal.rgb", { 32, 32 }));

terminal_button->on_click = [] (GButton&) {
pid_t child_pid = fork();
if (!child_pid) {
execve("/bin/Terminal", nullptr, nullptr);
ASSERT_NOT_REACHED();
}
};

auto* font_editor_button = new GButton(widget);
font_editor_button->set_relative_rect({ 60, 5, 50, 50 });
font_editor_button->set_icon(GraphicsBitmap::load_from_file("/res/icons/FontEditor.rgb", { 32, 32 }));

font_editor_button->on_click = [] (GButton&) {
pid_t child_pid = fork();
if (!child_pid) {
execve("/bin/FontEditor", nullptr, nullptr);
ASSERT_NOT_REACHED();
}
};

auto* guitest_editor_button = new GButton(widget);
guitest_editor_button->set_relative_rect({ 115, 5, 50, 50 });
guitest_editor_button->set_icon(GraphicsBitmap::load_from_file("/res/icons/generic.rgb", { 32, 32 }));

guitest_editor_button->on_click = [] (GButton&) {
pid_t child_pid = fork();
if (!child_pid) {
execve("/bin/guitest", nullptr, nullptr);
ASSERT_NOT_REACHED();
}
};

return window;
}
18 changes: 13 additions & 5 deletions LibGUI/GButton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,19 @@ void GButton::paint_event(GPaintEvent&)
painter.draw_line({ 2, height() - 3 }, { width() - 2, height() - 3 }, shadow_color);
}

if (!caption().is_empty()) {
auto text_rect = rect();
if (m_being_pressed)
text_rect.move_by(1, 1);
painter.draw_text(text_rect, caption(), Painter::TextAlignment::Center, Color::Black);
if (!caption().is_empty() || m_icon) {
auto content_rect = rect();
auto icon_location = m_icon ? content_rect.center().translated(-(m_icon->width() / 2), -(m_icon->height() / 2)) : Point();
if (m_being_pressed) {
content_rect.move_by(1, 1);
icon_location.move_by(1, 1);
}
if (m_icon) {
painter.blit_with_alpha(icon_location, *m_icon, m_icon->rect());
painter.draw_text(content_rect, caption(), Painter::TextAlignment::Center, Color::Black);
} else {
painter.draw_text(content_rect, caption(), Painter::TextAlignment::Center, Color::Black);
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions LibGUI/GButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "GWidget.h"
#include <AK/AKString.h>
#include <AK/Function.h>
#include <SharedGraphics/GraphicsBitmap.h>

class GButton final : public GWidget {
public:
Expand All @@ -12,6 +13,10 @@ class GButton final : public GWidget {
String caption() const { return m_caption; }
void set_caption(String&&);

void set_icon(RetainPtr<GraphicsBitmap>&& icon) { m_icon = move(icon); }
const GraphicsBitmap* icon() const { return m_icon.ptr(); }
GraphicsBitmap* icon() { return m_icon.ptr(); }

Function<void(GButton&)> on_click;

private:
Expand All @@ -23,6 +28,7 @@ class GButton final : public GWidget {
virtual const char* class_name() const override { return "GButton"; }

String m_caption;
RetainPtr<GraphicsBitmap> m_icon;
bool m_being_pressed { false };
bool m_tracking_cursor { false };
};
Expand Down
17 changes: 14 additions & 3 deletions SharedGraphics/Color.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,20 @@ class Color {
Color(byte r, byte g, byte b) : m_value((r << 16) | (g << 8) | b) { }
Color(RGBA32 rgba) : m_value(rgba) { }

int red() const { return (m_value >> 16) & 0xff; }
int green() const { return (m_value >> 8) & 0xff; }
int blue() const { return m_value & 0xff; }
byte red() const { return (m_value >> 16) & 0xff; }
byte green() const { return (m_value >> 8) & 0xff; }
byte blue() const { return m_value & 0xff; }
byte alpha() const { return (m_value >> 24) & 0xff; }

Color blend(Color source) const
{
RGBA32 redblue1 = ((0x100u - source.alpha()) * (m_value & 0xff00ff)) >> 8;
RGBA32 redblue2 = (source.alpha() * (source.m_value & 0xff00ff)) >> 8;
RGBA32 green1 = ((0x100u - source.alpha()) * (m_value & 0x00ff00)) >> 8;
RGBA32 green2 = (source.alpha() * (source.m_value & 0x00ff00)) >> 8;
return Color(((redblue1 | redblue2) & 0xff00ff) + ((green1 | green2) & 0x00ff00));
}


RGBA32 value() const { return m_value; }

Expand Down
57 changes: 56 additions & 1 deletion SharedGraphics/GraphicsBitmap.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
#include "GraphicsBitmap.h"
#include <AK/kmalloc.h>

#ifdef KERNEL
#include <Kernel/Process.h>
#include <Kernel/MemoryManager.h>
#include <WindowServer/WSMessageLoop.h>
#endif

#ifdef USERLAND
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#endif

#ifdef KERNEL
RetainPtr<GraphicsBitmap> GraphicsBitmap::create(Process& process, const Size& size)
{
Expand Down Expand Up @@ -39,6 +46,49 @@ RetainPtr<GraphicsBitmap> GraphicsBitmap::create_wrapper(const Size& size, RGBA3
return adopt(*new GraphicsBitmap(size, data));
}

RetainPtr<GraphicsBitmap> GraphicsBitmap::load_from_file(const String& path, const Size& size)
{
#ifdef USERLAND
int fd = open(path.characters(), O_RDONLY, 0644);
if (fd < 0) {
dbgprintf("open(%s) got fd=%d, failed: %s\n", path.characters(), fd, strerror(errno));
perror("open");
return nullptr;
}

auto* mapped_file = (RGBA32*)mmap(nullptr, size.area() * 4, PROT_READ, MAP_SHARED, fd, 0);
if (mapped_file == MAP_FAILED) {
int rc = close(fd);
ASSERT(rc == 0);
return nullptr;
}
#else
int error;
auto descriptor = VFS::the().open(path, error, 0, 0, *VFS::the().root_inode());
if (!descriptor) {
kprintf("Failed to load GraphicsBitmap from file (%s)\n", path.characters());
ASSERT_NOT_REACHED();
}
auto* region = current->allocate_file_backed_region(LinearAddress(), size.area() * 4, descriptor->inode(), ".rgb file", /*readable*/true, /*writable*/false);
region->page_in();
auto* mapped_file = (RGBA32*)region->laddr().get();
#endif


#ifdef USERLAND
int rc = close(fd);
ASSERT(rc == 0);
#endif
auto bitmap = create_wrapper(size, mapped_file);
#ifdef KERNEL
bitmap->m_server_region = region;
#else
bitmap->m_mmaped = true;
#endif

return bitmap;
}

GraphicsBitmap::GraphicsBitmap(const Size& size, RGBA32* data)
: m_size(size)
, m_data(data)
Expand All @@ -53,6 +103,11 @@ GraphicsBitmap::~GraphicsBitmap()
m_client_process->deallocate_region(*m_client_region);
if (m_server_region)
WSMessageLoop::the().server_process().deallocate_region(*m_server_region);
#else
if (m_mmaped) {
int rc = munmap(m_data, m_size.area() * 4);
ASSERT(rc == 0);
}
#endif
m_data = nullptr;
}
Expand Down
6 changes: 6 additions & 0 deletions SharedGraphics/GraphicsBitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Size.h"
#include <AK/Retainable.h>
#include <AK/RetainPtr.h>
#include <AK/AKString.h>

#ifdef KERNEL
#include "Process.h"
Expand All @@ -16,6 +17,7 @@ class GraphicsBitmap : public Retainable<GraphicsBitmap> {
static RetainPtr<GraphicsBitmap> create(Process&, const Size&);
#endif
static RetainPtr<GraphicsBitmap> create_wrapper(const Size&, RGBA32*);
static RetainPtr<GraphicsBitmap> load_from_file(const String& path, const Size&);
~GraphicsBitmap();

RGBA32* scanline(int y);
Expand All @@ -42,6 +44,10 @@ class GraphicsBitmap : public Retainable<GraphicsBitmap> {
RGBA32* m_data { nullptr };
size_t m_pitch { 0 };

#ifdef USERLAND
bool m_mmaped { false };
#endif

#ifdef KERNEL
WeakPtr<Process> m_client_process;
Region* m_client_region { nullptr };
Expand Down
28 changes: 28 additions & 0 deletions SharedGraphics/Painter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ void Painter::draw_focus_rect(const Rect& rect)
void Painter::blit(const Point& position, const GraphicsBitmap& source, const Rect& src_rect)
{
Rect dst_rect(position, src_rect.size());
dst_rect.move_by(m_translation);
dst_rect.intersect(m_clip_rect);

RGBA32* dst = m_target->scanline(dst_rect.y()) + dst_rect.x();
Expand All @@ -383,6 +384,33 @@ void Painter::blit(const Point& position, const GraphicsBitmap& source, const Re
}
}

void Painter::blit_with_alpha(const Point& position, const GraphicsBitmap& source, const Rect& src_rect)
{
Rect dst_rect(position, src_rect.size());
dst_rect.move_by(m_translation);
dst_rect.intersect(m_clip_rect);

RGBA32* dst = m_target->scanline(dst_rect.y()) + dst_rect.x();
const RGBA32* src = source.scanline(src_rect.top()) + src_rect.left();

const unsigned dst_skip = m_target->width();
const unsigned src_skip = source.width();

for (int i = dst_rect.height() - 1; i >= 0; --i) {
for (int x = 0; x < dst_rect.width(); ++x) {
byte alpha = Color(src[x]).alpha();
if (alpha == 0xff)
dst[x] = src[x];
else if (!alpha)
continue;
else
dst[x] = Color(dst[x]).blend(src[x]).value();
}
dst += dst_skip;
src += src_skip;
}
}

void Painter::set_clip_rect(const Rect& rect)
{
m_clip_rect = Rect::intersection(rect, m_target->rect());
Expand Down
Loading

0 comments on commit 887b4a7

Please sign in to comment.