Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ concurrency:

jobs:
native:
name: Native (${{ matrix.name }})
name: Desktop OpenGL 3.3 (${{ matrix.name }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
Expand Down Expand Up @@ -45,6 +45,7 @@ jobs:
libxkbcommon-dev \
ninja-build \
wayland-protocols \
xvfb \
xorg-dev

- name: Set up Python
Expand Down Expand Up @@ -72,6 +73,13 @@ jobs:
working-directory: WebGL2
run: ctest --preset desktop-release

- name: Smoke-test OpenGL 3.3 runtime
if: runner.os == 'Linux'
working-directory: WebGL2/build
env:
LIBGL_ALWAYS_SOFTWARE: '1'
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x720x24" ./MeshMakerWebGL2 --smoke-test

web:
name: WebAssembly and React
runs-on: ubuntu-latest
Expand Down
24 changes: 17 additions & 7 deletions MeshMakerWeb/e2e/script-editor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,25 @@ test.describe('Script Editor Keyboard', () => {
test('special keys are not blocked by keyboard isolation', async ({ page }) => {
// Verify that our GLFW keyboard isolation doesn't prevent
// Monaco from receiving keyboard events for text input.
await setEditorContent(page, '');
await focusEditor(page);
await setEditorContent(page, 'The quick brown fo');
await expect.poll(() => getEditorContent(page)).toBe('The quick brown fo');

// Type a sentence — if GLFW eats any keys, characters would be missing
await page.keyboard.type('The quick brown fox', { delay: 20 });
await page.waitForTimeout(200);
// Position the real Monaco editor at the end and send one browser key event.
// A single key still exercises keyboard isolation without relying on a
// timing-sensitive stream of synthetic typing under CI load.
await page.evaluate(() => {
const editor = (window as any).__meshmakerScriptEditor?.getEditor();
const model = editor?.getModel();
if (!editor || !model) {
throw new Error('Monaco editor is not ready');
}

const content = await getEditorContent(page);
expect(content).toContain('The quick brown fox');
editor.setPosition(model.getFullModelRange().getEndPosition());
editor.focus();
});
await page.keyboard.press('x');

await expect.poll(() => getEditorContent(page)).toBe('The quick brown fox');
});

test('typing in Monaco does not trigger GLFW shortcuts', async ({ page }) => {
Expand Down
3 changes: 3 additions & 0 deletions WebGL2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ sudo apt install cmake libglfw3-dev
# Configure and build
cmake -B build
cmake --build build

# Optional: verify context creation, shaders, GPU buffers, and one rendered frame.
./build/MeshMakerWebGL2 --smoke-test
```

### Desktop (macOS)
Expand Down
46 changes: 45 additions & 1 deletion WebGL2/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <algorithm>
#include <climits>
#include <cmath>
#include <string>

#include "Camera.h"
#include "Mesh2.h"
Expand Down Expand Up @@ -3494,7 +3495,12 @@ void emscriptenMainLoop() {
}
#endif

#ifdef EMSCRIPTEN_BUILD
int main() {
#else
int main(int argc, char* argv[]) {
const bool smokeTest = argc > 1 && std::string(argv[1]) == "--smoke-test";
#endif
// Initialize GLFW
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl;
Expand All @@ -3516,6 +3522,9 @@ int main() {
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
if (smokeTest) {
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
}
#endif

// Create window
Expand All @@ -3536,6 +3545,24 @@ int main() {
std::cerr << "Failed to initialize GLAD" << std::endl;
return -1;
}

GLint contextMajor = 0;
GLint contextMinor = 0;
GLint contextProfile = 0;
glGetIntegerv(GL_MAJOR_VERSION, &contextMajor);
glGetIntegerv(GL_MINOR_VERSION, &contextMinor);
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &contextProfile);
if (contextMajor < 3 || (contextMajor == 3 && contextMinor < 3) ||
(contextProfile & GL_CONTEXT_CORE_PROFILE_BIT) == 0) {
std::cerr << "OpenGL 3.3 Core is required, got "
<< contextMajor << '.' << contextMinor << std::endl;
glfwDestroyWindow(g_app.window);
glfwTerminate();
return -1;
}

std::cout << "OpenGL context: " << glGetString(GL_VERSION) << '\n'
<< "OpenGL renderer: " << glGetString(GL_RENDERER) << std::endl;
#endif

// Get initial framebuffer size and content scale for High DPI support
Expand Down Expand Up @@ -3656,8 +3683,21 @@ int main() {
#ifdef EMSCRIPTEN_BUILD
emscripten_set_main_loop(emscriptenMainLoop, 0, 1);
#else
while (!glfwWindowShouldClose(g_app.window)) {
int exitCode = 0;
if (smokeTest) {
mainLoop();
glFinish();
const GLenum error = glGetError();
if (error == GL_NO_ERROR) {
std::cout << "OpenGL 3.3 runtime smoke test passed" << std::endl;
} else {
std::cerr << "OpenGL runtime error: 0x" << std::hex << error << std::endl;
exitCode = 1;
}
} else {
while (!glfwWindowShouldClose(g_app.window)) {
mainLoop();
}
}
#endif

Expand All @@ -3669,7 +3709,11 @@ int main() {
glfwDestroyWindow(g_app.window);
glfwTerminate();

#ifdef EMSCRIPTEN_BUILD
return 0;
#else
return exitCode;
#endif
}

// ============================================================================
Expand Down
Loading