diff --git a/flake.nix b/flake.nix index bb5fab3..d369ec3 100644 --- a/flake.nix +++ b/flake.nix @@ -3,29 +3,33 @@ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - outputs = { - self, - nixpkgs, - }: let - system = "x86_64-linux"; - pkgs = import nixpkgs {inherit system;}; - in { - devShells.${system}.default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ - gcc - clang-tools - lldb + outputs = + { + self, + nixpkgs, + }: + let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + in + { + devShells.${system}.default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + gcc + clang-tools + lldb - meson - ninja - pkg-config + meson + ninja + pkg-config - gtk4 + gtk4 + libepoxy - gdb - ]; + gdb + ]; - shellHook = ""; + shellHook = ""; + }; }; - }; } diff --git a/meson.build b/meson.build index 5e4838c..07049d3 100644 --- a/meson.build +++ b/meson.build @@ -11,6 +11,7 @@ project( ) gtk = dependency('gtk4', required: true) +epoxy = dependency('epoxy') src = files( 'src/main.c', @@ -25,7 +26,7 @@ src = files( executable( 'vektor', src, - dependencies: [gtk], + dependencies: [gtk,epoxy], link_args: ['-lm'], install: true, ) diff --git a/src/ui/uicontroller.c b/src/ui/uicontroller.c index 2f6fb3d..84da301 100644 --- a/src/ui/uicontroller.c +++ b/src/ui/uicontroller.c @@ -26,7 +26,7 @@ void vektor_uictrl_init(GtkApplication* app, VektorWidgetState* stateOut) { stateOut->workspacePaned = GTK_PANED(gtk_builder_get_object(builder, "workspace_paned")); stateOut->workspaceCanvas = - GTK_PICTURE(gtk_builder_get_object(builder, "workspace")); + GTK_GL_AREA(gtk_builder_get_object(builder, "workspace")); stateOut->workspaceButtonLinetool = GTK_BUTTON(gtk_builder_get_object(builder, "button_linetool")); diff --git a/src/ui/uicontroller.h b/src/ui/uicontroller.h index 73dd7b5..723274e 100644 --- a/src/ui/uicontroller.h +++ b/src/ui/uicontroller.h @@ -10,7 +10,7 @@ all the widgets used in internal logic of the program typedef struct VektorWidgetState { GtkWindow* window; GtkPaned* workspacePaned; - GtkPicture* workspaceCanvas; + GtkGLArea* workspaceCanvas; GtkButton* workspaceButtonLinetool; diff --git a/src/ui/vektorcanvas.c b/src/ui/vektorcanvas.c index 074d461..c7a4990 100644 --- a/src/ui/vektorcanvas.c +++ b/src/ui/vektorcanvas.c @@ -1,3 +1,4 @@ +#include "epoxy/gl.h" #include "gtk/gtk.h" #include "../core/raster.h" @@ -8,6 +9,114 @@ #define VKTR_CANVAS_HEIGHT 400 #define VKTR_CANVAS_SIZE (VKTR_CANVAS_WIDTH * VKTR_CANVAS_HEIGHT * 4) +static GLuint shader_program; +static GLuint vao; + +static const char* vertex_shader_src = + "#version 300 es\n" // <- ES version + "layout(location = 0) in vec2 aPos;\n" + "void main() {\n" + " gl_Position = vec4(aPos, 0.0, 1.0);\n" + "}\n"; + +static const char* fragment_shader_src = + "#version 300 es\n" // <- ES version + "precision mediump float;\n" // required in ES for fragment color + "out vec4 FragColor;\n" + "void main() {\n" + " FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + "}\n"; + +static GLuint compile_shader(GLenum type, const char* src) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + char info[512]; + glGetShaderInfoLog(shader, 512, NULL, info); + g_error("Shader compile failed: %s", info); + } + return shader; +} + +static void init_shader(void) { + GLuint vertex = compile_shader(GL_VERTEX_SHADER, vertex_shader_src); + GLuint fragment = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_src); + + shader_program = glCreateProgram(); + glAttachShader(shader_program, vertex); + glAttachShader(shader_program, fragment); + glLinkProgram(shader_program); + + GLint success; + glGetProgramiv(shader_program, GL_LINK_STATUS, &success); + if (!success) { + char info[512]; + glGetProgramInfoLog(shader_program, 512, NULL, info); + g_error("Shader link failed: %s", info); + } + + glDeleteShader(vertex); + glDeleteShader(fragment); +} + +static void init_geometry(void) { + // Vertices for a rectangle in NDC coordinates + float vertices[] = { + -0.5f, -0.5f, // bottom-left + 0.5f, -0.5f, // bottom-right + 0.5f, 0.5f, // top-right + -0.5f, 0.5f // top-left + }; + unsigned int indices[] = {0, 1, 2, 2, 3, 0}; + + GLuint vbo, ebo; + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glGenBuffers(1, &ebo); + + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, + GL_STATIC_DRAW); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), + (void*)0); + glEnableVertexAttribArray(0); + + glBindVertexArray(0); +} + +static gboolean render(GtkGLArea* area, GdkGLContext* context) { + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(shader_program); + glBindVertexArray(vao); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + glUseProgram(0); + + return TRUE; +} + +static void realize(GtkGLArea* area, gpointer user_data) { + gtk_gl_area_make_current(area); + + if (gtk_gl_area_get_error(area) != NULL) + return; // context creation failed + + init_shader(); + init_geometry(); +} + void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut) { canvasOut->canvasWidget = state->workspaceCanvas; canvasOut->width = VKTR_CANVAS_WIDTH; @@ -20,10 +129,14 @@ void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut) { VKTR_CANVAS_WIDTH, VKTR_CANVAS_HEIGHT, GDK_MEMORY_R8G8B8A8, canvasOut->canvasPixelBytes, VKTR_CANVAS_WIDTH * 4); - gtk_picture_set_paintable(canvasOut->canvasWidget, - GDK_PAINTABLE(canvasOut->canvasTexture)); - gtk_picture_set_content_fit(GTK_PICTURE(canvasOut->canvasWidget), - GTK_CONTENT_FIT_CONTAIN); + g_signal_connect(canvasOut->canvasWidget, "realize", G_CALLBACK(realize), + NULL); + g_signal_connect(canvasOut->canvasWidget, "render", G_CALLBACK(render), + NULL); + // gtk_picture_set_paintable(canvasOut->canvasWidget, + // GDK_PAINTABLE(canvasOut->canvasTexture)); + // gtk_picture_set_content_fit(GTK_PICTURE(canvasOut->canvasWidget), + // GTK_CONTENT_FIT_CONTAIN); // g_object_unref(bytes); } @@ -38,8 +151,8 @@ void vektor_canvas_update(VektorCanvas* canvas) { canvas->width, canvas->height, GDK_MEMORY_R8G8B8A8, canvas->canvasPixelBytes, canvas->width * 4); - gtk_picture_set_paintable(canvas->canvasWidget, - GDK_PAINTABLE(canvas->canvasTexture)); + // gtk_picture_set_paintable(canvas->canvasWidget, + // GDK_PAINTABLE(canvas->canvasTexture)); } void vektor_canvas_fill(VektorCanvas* canvas, VektorColor color) { diff --git a/src/ui/vektorcanvas.h b/src/ui/vektorcanvas.h index abf9560..e66c369 100644 --- a/src/ui/vektorcanvas.h +++ b/src/ui/vektorcanvas.h @@ -3,10 +3,11 @@ #include "../core/raster.h" #include "../util/color.h" +#include "gtk/gtk.h" #include "uicontroller.h" typedef struct VektorCanvas { - GtkPicture* canvasWidget; + GtkGLArea* canvasWidget; // texture related stuff guchar* canvasPixels; diff --git a/ui/main.ui b/ui/main.ui index fa33915..718ee6c 100644 --- a/ui/main.ui +++ b/ui/main.ui @@ -45,11 +45,16 @@ - + + + + true + true