From bbf4c88d0d04f500d134dce8d4cbc092b95dd483 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 8 May 2020 15:09:21 -0700 Subject: [PATCH] Add an example showing how to use the Pathfinder C API with GLFW Closes #258. --- .gitignore | 1 + c/cbindgen.toml | 8 +- examples/c_canvas_glfw_minimal/Makefile | 47 ++++++++ .../c_canvas_glfw_minimal.c | 100 ++++++++++++++++++ 4 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 examples/c_canvas_glfw_minimal/Makefile create mode 100644 examples/c_canvas_glfw_minimal/c_canvas_glfw_minimal.c diff --git a/.gitignore b/.gitignore index cdc7a13c..8e2eff39 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ target /site/dist node_modules /examples/c_canvas_minimal/build +/examples/c_canvas_glfw_minimal/build /shaders/build /c/build /examples/macos_app/Pathfinder\ Example.xcodeproj/project.xcworkspace/xcuserdata/* diff --git a/c/cbindgen.toml b/c/cbindgen.toml index 38119545..f8af9071 100644 --- a/c/cbindgen.toml +++ b/c/cbindgen.toml @@ -5,17 +5,15 @@ header = """\ #ifndef PF_PATHFINDER_H #define PF_PATHFINDER_H -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(__OBJC__) #include +#else +typedef struct CAMetalLayerPrivate CAMetalLayer; #endif #ifdef __cplusplus extern \"C\" { #endif - -#ifndef __APPLE__ -typedef struct CAMetalLayerPrivate CAMetalLayer; -#endif """ trailer = """\ #ifdef __cplusplus diff --git a/examples/c_canvas_glfw_minimal/Makefile b/examples/c_canvas_glfw_minimal/Makefile new file mode 100644 index 00000000..d0182c39 --- /dev/null +++ b/examples/c_canvas_glfw_minimal/Makefile @@ -0,0 +1,47 @@ +OBJ_DIR?=build +TARGET_DIR?=build +SRC_DIR?=. +RUST_TARGET_DIR?=../../target +RUST_SRC_DIR?=../../c +RUSTFLAGS?= + +CFLAGS?=-Wall -g -I../../c/build/include +LIBS?=-lpathfinder_c -lglfw +MKDIR?=mkdir -p +RM?=rm +CARGO?=cargo + +UNAME=$(shell uname -s) +ifeq ($(UNAME),Darwin) + # FIXME(pcwalton): Don't link against HarfBuzz!! + LIBS+=-framework OpenGL -framework CoreFoundation -framework CoreGraphics -framework CoreText + LIBS+=-framework Metal -lharfbuzz +else + LIBS+=-lGL +endif + +ifeq ($(DEBUG),) + CFLAGS+=-O2 + LDFLAGS?=-L$(RUST_TARGET_DIR)/release + CARGOFLAGS?=--release +else + CFLAGS+=-O0 + LDFLAGS?=-L$(RUST_TARGET_DIR)/debug + CARGOFLAGS?= +endif + +all: $(TARGET_DIR)/c_canvas_glfw_minimal + +.PHONY: clean rustlib + +$(TARGET_DIR)/c_canvas_glfw_minimal: $(OBJ_DIR)/c_canvas_glfw_minimal.o rustlib + $(MKDIR) $(TARGET_DIR) && $(CC) $(LDFLAGS) $(LIBS) -o $@ $< + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c + $(MKDIR) $(OBJ_DIR) && $(CC) -c $(CFLAGS) -o $@ $< + +rustlib: + cd $(RUST_SRC_DIR) && RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build $(CARGOFLAGS) + +clean: + $(RM) -rf $(TARGET_DIR) && $(RM) -rf $(OBJ_DIR) diff --git a/examples/c_canvas_glfw_minimal/c_canvas_glfw_minimal.c b/examples/c_canvas_glfw_minimal/c_canvas_glfw_minimal.c new file mode 100644 index 00000000..79345337 --- /dev/null +++ b/examples/c_canvas_glfw_minimal/c_canvas_glfw_minimal.c @@ -0,0 +1,100 @@ +// pathfinder/examples/c_canvas_glfw_minimal/c_canvas_glfw_minimal.c +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#include +#include +#include +#include +#include +#include + +static void HandleGLFWError(int errorCode, const char *description); +static const void *LoadGLFunction(const char *name, void *userdata); +static void HandleKeypress(GLFWwindow *window, int key, int scancode, int action, int mods); + +int main(int argc, const char **argv) { + // Set up GLFW. + GLFWwindow *window; + if (!glfwInit()) + return 1; + glfwSetErrorCallback(HandleGLFWError); + + // Make sure we have at least a GL 3.0 context. Pathfinder requires this. + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + + // Open a window. + window = glfwCreateWindow(640, 480, "Minimal canvas example (GLFW/C API)", NULL, NULL); + + // Make the OpenGL context current. + glfwMakeContextCurrent(window); + + // Create a Pathfinder renderer. + PFGLLoadWith(LoadGLFunction, NULL); + PFGLDestFramebufferRef dest_framebuffer = + PFGLDestFramebufferCreateFullWindow(&(PFVector2I){640, 480}); + PFGLRendererRef renderer = PFGLRendererCreate(PFGLDeviceCreate(PF_GL_VERSION_GL3, 0), + PFFilesystemResourceLoaderLocate(), + dest_framebuffer, + &(PFRendererOptions){ + (PFColorF){1.0, 1.0, 1.0, 1.0}, PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR + }); + + // Make a canvas. We're going to draw a house. + PFCanvasRef canvas = PFCanvasCreate(PFCanvasFontContextCreateWithSystemSource(), + &(PFVector2F){640.0f, 480.0f}); + + // Set line width. + PFCanvasSetLineWidth(canvas, 10.0f); + + // Draw walls. + PFCanvasStrokeRect(canvas, &(PFRectF){{75.0f, 140.0f}, {225.0f, 250.0f}}); + + // Draw door. + PFCanvasFillRect(canvas, &(PFRectF){{130.0f, 190.0f}, {170.0f, 250.0f}}); + + // Draw roof. + PFPathRef path = PFPathCreate(); + PFPathMoveTo(path, &(PFVector2F){50.0, 140.0}); + PFPathLineTo(path, &(PFVector2F){150.0, 60.0}); + PFPathLineTo(path, &(PFVector2F){250.0, 140.0}); + PFPathClosePath(path); + PFCanvasStrokePath(canvas, path); + + // Render the canvas to screen. + PFSceneRef scene = PFCanvasCreateScene(canvas); + PFSceneProxyRef scene_proxy = PFSceneProxyCreateFromSceneAndRayonExecutor(scene); + PFSceneProxyBuildAndRenderGL(scene_proxy, renderer, PFBuildOptionsCreate()); + glfwSwapBuffers(window); + + // Wait for a keypress. + glfwSetKeyCallback(window, HandleKeypress); + while (!glfwWindowShouldClose(window)) + glfwPollEvents(); + + // Finish up. + glfwTerminate(); + return 0; +} + +static void HandleGLFWError(int errorCode, const char *description) { + fprintf(stderr, "GLFW error: %s [%d]\n", description, errorCode); + exit(1); +} + +static void HandleKeypress(GLFWwindow *window, int key, int scancode, int action, int mods) { + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static const void *LoadGLFunction(const char *name, void *userdata) { + return glfwGetProcAddress(name); +}