cocoa!
This commit is contained in:
parent
0b2b8dfdb1
commit
7a0538b2ea
432
vinci-cocoa.m
Normal file
432
vinci-cocoa.m
Normal file
@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Vinci
|
||||
*
|
||||
* Copyright (C) 2025 Orastron Srl unipersonale
|
||||
*
|
||||
* Vinci is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Vinci is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Vinci. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* File author: Paolo Marrone
|
||||
*/
|
||||
|
||||
#include "vinci.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface VinciView : NSView
|
||||
{
|
||||
NSTrackingArea *tracking;
|
||||
@public window *win;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface VinciViewController : NSViewController
|
||||
{
|
||||
@public window *win;
|
||||
}
|
||||
@property (nonatomic, strong) VinciView *vinciView;
|
||||
@end
|
||||
|
||||
struct vinci {
|
||||
window *windows;
|
||||
NSApplication *app;
|
||||
NSAutoreleasePool *pool;
|
||||
char standalone; // If any view has a parent, it is not standalone
|
||||
};
|
||||
|
||||
struct window {
|
||||
vinci *g;
|
||||
window *next;
|
||||
NSWindow *nswindow;
|
||||
VinciView *view;
|
||||
VinciViewController *controller;
|
||||
unsigned char *img;
|
||||
NSImage *nsImage;
|
||||
void *data;
|
||||
window_cbs cbs;
|
||||
NSRect default_frame;
|
||||
};
|
||||
|
||||
static uint32_t get_mouse_state(NSEvent* e) {
|
||||
(void) e;
|
||||
return (uint32_t) [NSEvent pressedMouseButtons];
|
||||
}
|
||||
|
||||
@implementation VinciView
|
||||
|
||||
- (void) updateTrackingAreas {
|
||||
|
||||
[self removeTrackingArea];
|
||||
|
||||
tracking = [[NSTrackingArea alloc] initWithRect:[self bounds]
|
||||
options:NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
[self addTrackingArea:tracking];
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void) removeTrackingArea {
|
||||
if (tracking) {
|
||||
[super removeTrackingArea:tracking];
|
||||
[tracking release];
|
||||
tracking = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) mouseDown : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_press)
|
||||
w->cbs.on_mouse_press(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) mouseUp : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_release)
|
||||
w->cbs.on_mouse_release(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) mouseMoved : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_move)
|
||||
w->cbs.on_mouse_move(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) rightMouseDown : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_press)
|
||||
w->cbs.on_mouse_press(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) rightMouseUp : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_release)
|
||||
w->cbs.on_mouse_release(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) rightMouseDragged : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_move)
|
||||
w->cbs.on_mouse_move(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) otherMouseDown : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_press)
|
||||
w->cbs.on_mouse_press(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) otherMouseUp : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_release)
|
||||
w->cbs.on_mouse_release(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) otherMouseDragged : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_move)
|
||||
w->cbs.on_mouse_move(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) mouseDragged : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
if (w->cbs.on_mouse_move)
|
||||
w->cbs.on_mouse_move(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) mouseEntered : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = e.locationInWindow;
|
||||
if (w->cbs.on_mouse_enter)
|
||||
w->cbs.on_mouse_enter(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) mouseExited : (NSEvent *) e {
|
||||
window *w = self->win;
|
||||
NSPoint p = e.locationInWindow;
|
||||
if (w->cbs.on_mouse_leave)
|
||||
w->cbs.on_mouse_leave(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
}
|
||||
|
||||
- (void) keyDown : (NSEvent *) e {
|
||||
//window *w = self->win;
|
||||
//NSPoint p = e.locationInWindow;
|
||||
//on_key_press(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||
// TODO
|
||||
}
|
||||
|
||||
- (void) keyUp : (NSEvent *) e {
|
||||
//window *w = self->win;
|
||||
//NSPoint p = e.locationInWindow;
|
||||
// TODO
|
||||
}
|
||||
|
||||
// custom
|
||||
- (void) vinci_resized {
|
||||
window *w = self->win;
|
||||
NSSize size = [w->view bounds].size;
|
||||
w->img = (unsigned char*)realloc(w->img, (uint32_t) (size.width * size.height) * 4);
|
||||
if (w->cbs.on_window_resize)
|
||||
w->cbs.on_window_resize(w, size.width, size.height);
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)removeFromSuperview {
|
||||
[super removeFromSuperview];
|
||||
window *w = self->win;
|
||||
if (w->cbs.on_window_close)
|
||||
w->cbs.on_window_close(w);
|
||||
}
|
||||
|
||||
// Custom method for standalone application
|
||||
- (void)windowWillClose:(NSNotification *)notification {
|
||||
window *w = self->win;
|
||||
if (w->cbs.on_window_close)
|
||||
w->cbs.on_window_close(w);
|
||||
}
|
||||
|
||||
- (void) drawRect:(NSRect) r {
|
||||
[self->win->nsImage drawInRect:r fromRect:r operation:NSCompositingOperationCopy fraction:1.0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation VinciViewController
|
||||
|
||||
- (void)loadView {
|
||||
self.vinciView = [[VinciView alloc] initWithFrame:self->win->default_frame];
|
||||
self.view = self.vinciView;
|
||||
}
|
||||
|
||||
- (void)viewDidLayout {
|
||||
[super viewDidLayout];
|
||||
[self.vinciView vinci_resized];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
vinci* vinci_new(void) {
|
||||
vinci *g = (vinci*) malloc(sizeof(vinci));
|
||||
if (g == NULL)
|
||||
return NULL;
|
||||
g->windows = NULL;
|
||||
g->pool = [[NSAutoreleasePool alloc] init];
|
||||
g->app = [NSApplication sharedApplication];
|
||||
//[g->app setActivationPolicy:NSApplicationActivationPolicyRegular]; // TODO: check if this is a problem when not standalone
|
||||
g->standalone = 1;
|
||||
return g;
|
||||
}
|
||||
|
||||
void vinci_destroy(vinci *g) {
|
||||
free(g);
|
||||
}
|
||||
|
||||
void vinci_idle(vinci *g) {
|
||||
(void) g;
|
||||
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
NSDate *expiration = [NSDate now];
|
||||
|
||||
while (true) {
|
||||
NSEvent *event = [app nextEventMatchingMask:NSEventMaskAny
|
||||
untilDate:expiration
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
if (!event)
|
||||
break;
|
||||
[app sendEvent:event];
|
||||
}
|
||||
}
|
||||
|
||||
window* window_new(vinci *g, void *parent, uint32_t width, uint32_t height, window_cbs *cbs) {
|
||||
|
||||
window *ret = (window*) malloc(sizeof(window));
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
|
||||
ret->default_frame = NSMakeRect(0, 0, width, height);
|
||||
|
||||
VinciViewController *controller = [[VinciViewController alloc] init];
|
||||
controller->win = ret;
|
||||
VinciView *view = (VinciView*) controller.view;
|
||||
|
||||
view->win = ret;
|
||||
|
||||
ret->g = g;
|
||||
ret->view = view;
|
||||
ret->controller = controller;
|
||||
|
||||
if (parent) {
|
||||
[((NSView*)parent) addSubview:view positioned:NSWindowAbove relativeTo:nil];
|
||||
ret->nswindow = [(NSView*)parent window];
|
||||
g->standalone = 0;
|
||||
}
|
||||
else {
|
||||
int style = NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable;
|
||||
ret->nswindow = [[NSWindow alloc] initWithContentRect:ret->default_frame
|
||||
styleMask:style
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
[ret->nswindow setTitle:@"Vinci window"];
|
||||
[ret->nswindow setContentView:view];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:view
|
||||
selector:@selector(windowWillClose:)
|
||||
name:NSWindowWillCloseNotification
|
||||
object:ret->nswindow];
|
||||
}
|
||||
|
||||
ret->img = (unsigned char*) malloc(width * height * 4);
|
||||
ret->data = NULL;
|
||||
ret->nsImage = NULL;
|
||||
ret->next = NULL;
|
||||
ret->cbs = *cbs;
|
||||
|
||||
[view autorelease];
|
||||
|
||||
if (g->windows == NULL)
|
||||
g->windows = ret;
|
||||
else {
|
||||
window *cur = g->windows;
|
||||
while (cur->next != NULL)
|
||||
cur = cur->next;
|
||||
cur->next = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void window_free(window *w) {
|
||||
if (w->g->windows == w) {
|
||||
w->g->windows = w->next;
|
||||
}
|
||||
else {
|
||||
window *cur = w->g->windows;
|
||||
while (cur != NULL && cur->next != w)
|
||||
cur = cur->next;
|
||||
if (cur == NULL)
|
||||
return;
|
||||
cur->next = w->next;
|
||||
}
|
||||
|
||||
[w->view removeTrackingArea];
|
||||
free(w->img);
|
||||
free(w);
|
||||
}
|
||||
|
||||
void window_draw(window *w, unsigned char *img, int32_t dx, int32_t dy, int32_t dw, int32_t dh, int32_t wx, int32_t wy, int32_t width, int32_t height) {
|
||||
if (w->img == NULL)
|
||||
return;
|
||||
|
||||
NSSize size = [w->view bounds].size;
|
||||
|
||||
uint32_t iw = wy * size.width + wx;
|
||||
uint32_t o = dy * dw + dx;
|
||||
uint32_t p1 = dw;
|
||||
uint32_t p2 = size.width;
|
||||
|
||||
for (int32_t y = dy; y < dy + height && y < dh; y++) {
|
||||
memcpy(w->img + iw * 4, img + o * 4, width * 4);
|
||||
iw += p2;
|
||||
o += p1;
|
||||
}
|
||||
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData(
|
||||
NULL, // void *info
|
||||
w->img, // const void *data
|
||||
size.width * size.height * 4, // size_t size
|
||||
NULL // CGDataProviderReleaseDataCallback releaseData
|
||||
);
|
||||
|
||||
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
|
||||
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
|
||||
|
||||
CGImageRef imageRef = CGImageCreate(
|
||||
size.width, // size_t width
|
||||
size.height, // size_t height
|
||||
8, // size_t bitsPerComponent
|
||||
4 * 8, // size_t bitsPerPixel
|
||||
size.width * 4, // size_t bytesPerRow
|
||||
colorSpaceRef, // CGColorSpaceRef space
|
||||
bitmapInfo, // CGBitmapInfo bitmapInfo
|
||||
provider, // CGDataProviderRef provider
|
||||
NULL, // const CGFloat *decode
|
||||
NO, // bool shouldInterpolate
|
||||
renderingIntent // CGColorRenderingIntent intent
|
||||
);
|
||||
|
||||
NSImage* image = [[NSImage alloc] initWithCGImage: imageRef size:NSZeroSize];
|
||||
[w->nsImage release];
|
||||
w->nsImage = image;
|
||||
|
||||
[w->view setNeedsDisplayInRect:NSMakeRect(wx, size.height - (wy + height), width, height)];
|
||||
|
||||
CGDataProviderRelease(provider);
|
||||
CGColorSpaceRelease(colorSpaceRef);
|
||||
CGImageRelease(imageRef);
|
||||
}
|
||||
|
||||
void window_resize (window *w, uint32_t width, uint32_t height) {
|
||||
NSSize newSize = NSMakeSize(width, height);
|
||||
[w->view setFrameSize:newSize]; // TODO: this sends NSViewFrameDidChangeNotification, we should handle that instead in 1 place
|
||||
w->img = (unsigned char*)realloc(w->img, width * height * 4);
|
||||
w->view.needsDisplay = YES;
|
||||
if (w->cbs.on_window_resize)
|
||||
w->cbs.on_window_resize(w, width, height); // view should be automatically informed
|
||||
}
|
||||
|
||||
void window_move(window *w, uint32_t x, uint32_t y) {
|
||||
(void) w;
|
||||
(void) x;
|
||||
(void) y;
|
||||
}
|
||||
|
||||
void* window_get_handle(window *w) {
|
||||
return w->view;
|
||||
}
|
||||
|
||||
uint32_t window_get_width(window *w) {
|
||||
return [w->view bounds].size.width;
|
||||
}
|
||||
uint32_t window_get_height(window *w) {
|
||||
return [w->view bounds].size.height;
|
||||
}
|
||||
|
||||
void window_show(window *w) {
|
||||
// Can we? nswindow comes from outside
|
||||
[w->nswindow setIsVisible:YES];
|
||||
}
|
||||
|
||||
void window_hide(window *w) {
|
||||
(void) w;
|
||||
//[w->nswindow setIsVisible:NO];
|
||||
}
|
||||
|
||||
void window_set_data(window *w, void *data) {
|
||||
w->data = data;
|
||||
}
|
||||
|
||||
void *window_get_data(window *w) {
|
||||
return w->data;
|
||||
}
|
2
vinci.h
2
vinci.h
@ -44,7 +44,7 @@ typedef struct window_cbs {
|
||||
|
||||
vinci* vinci_new (void);
|
||||
void vinci_destroy (vinci *g);
|
||||
void vinci_idle (vinci *g);
|
||||
void vinci_idle (vinci *g); // Non blocking: call it repeatedly with a timer
|
||||
window* window_new (vinci *g, void* p, uint32_t width, uint32_t height, window_cbs *cbs);
|
||||
void window_free (window *w);
|
||||
void window_draw (window *w, unsigned char *data, int32_t dx, int32_t dy, int32_t dw, int32_t dh, int32_t wx, int32_t wy, int32_t width, int32_t height);
|
||||
|
Loading…
Reference in New Issue
Block a user