added osx cocoa support files
This commit is contained in:
parent
595dad2e70
commit
d7a04aea8c
253
libobs-opengl/gl-cocoa.m
Normal file
253
libobs-opengl/gl-cocoa.m
Normal file
@ -0,0 +1,253 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2013 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This program 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include "gl-subsystem.h"
|
||||
#include <OpenGL/OpenGL.h>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
|
||||
//#include "util/darray.h"
|
||||
|
||||
|
||||
struct gl_windowinfo {
|
||||
NSView *view;
|
||||
};
|
||||
|
||||
struct gl_platform {
|
||||
NSOpenGLContext *context;
|
||||
GLuint vao;
|
||||
struct gs_swap_chain swap;
|
||||
};
|
||||
|
||||
static NSOpenGLContext *gl_context_create(struct gs_init_data *info)
|
||||
{
|
||||
unsigned attrib_count = 0;
|
||||
|
||||
#define ADD_ATTR(x) { attributes[attrib_count++] = (NSOpenGLPixelFormatAttribute)x; }
|
||||
#define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); }
|
||||
|
||||
NSOpenGLPixelFormatAttribute attributes[40];
|
||||
|
||||
switch(info->num_backbuffers) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
ADD_ATTR(NSOpenGLPFADoubleBuffer);
|
||||
break;
|
||||
case 2:
|
||||
ADD_ATTR(NSOpenGLPFATripleBuffer);
|
||||
break;
|
||||
default:
|
||||
blog(LOG_ERROR, "Requested backbuffers (%d) not supported", info->num_backbuffers);
|
||||
}
|
||||
|
||||
ADD_ATTR(NSOpenGLPFAClosestPolicy);
|
||||
ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core);
|
||||
|
||||
int color_bits = 0;//get_color_format_bits(info->format);
|
||||
if(color_bits == 0) color_bits = 24;
|
||||
else if(color_bits < 15) color_bits = 15;
|
||||
|
||||
ADD_ATTR2(NSOpenGLPFAColorSize, color_bits);
|
||||
|
||||
ADD_ATTR2(NSOpenGLPFAAlphaSize, 8);
|
||||
|
||||
ADD_ATTR2(NSOpenGLPFADepthSize, 16);
|
||||
|
||||
ADD_ATTR(0);
|
||||
|
||||
#undef ADD_ATTR2
|
||||
#undef ADD_ATTR
|
||||
|
||||
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];;
|
||||
if(!pf) {
|
||||
blog(LOG_ERROR, "Failed to create pixel format");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
||||
[pf release];
|
||||
if(!context) {
|
||||
blog(LOG_ERROR, "Failed to create context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
[context setView:info->view];
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static inline void required_extension_error(const char *extension)
|
||||
{
|
||||
blog(LOG_ERROR, "OpenGL extension %s is required", extension);
|
||||
}
|
||||
|
||||
static bool gl_init_extensions(device_t device)
|
||||
{
|
||||
glewExperimental=true;
|
||||
GLenum error = glewInit();
|
||||
if(error != GLEW_OK) {
|
||||
blog(LOG_ERROR, "glewInit failed, %u\n%s\n", error, glewGetErrorString(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!GLEW_VERSION_2_1) {
|
||||
blog(LOG_ERROR, "OpenGL 2.1 minimum required by the graphics "
|
||||
"adapter");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!GLEW_ARB_framebuffer_object) {
|
||||
required_extension_error("GL_ARB_framebuffer_object");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!GLEW_ARB_separate_shader_objects) {
|
||||
required_extension_error("GL_ARB_separate_shader_objects");
|
||||
return false;
|
||||
}
|
||||
|
||||
glGetError(); //something inside glew produces error code 1280 (invalid enum)
|
||||
|
||||
device->copy_type = COPY_TYPE_FBO_BLIT;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gl_init_default_swap(struct gl_platform *plat, device_t dev,
|
||||
struct gs_init_data *info)
|
||||
{
|
||||
if(!(plat->context = gl_context_create(info)))
|
||||
return false;
|
||||
|
||||
plat->swap.device = dev;
|
||||
plat->swap.info = *info;
|
||||
plat->swap.wi = gl_windowinfo_create(info);
|
||||
|
||||
return plat->swap.wi != NULL;
|
||||
}
|
||||
|
||||
struct gl_platform *gl_platform_create(device_t device,
|
||||
struct gs_init_data *info)
|
||||
{
|
||||
struct gl_platform *plat = bmalloc(sizeof(struct gl_platform));
|
||||
memset(plat, 0, sizeof(struct gl_platform));
|
||||
|
||||
if(!gl_init_default_swap(plat, device, info))
|
||||
goto fail;
|
||||
|
||||
[plat->context makeCurrentContext];
|
||||
|
||||
if(!gl_init_extensions(device))
|
||||
goto fail;
|
||||
|
||||
gl_gen_vertex_arrays(1, &plat->vao);
|
||||
gl_bind_vertex_array(plat->vao);
|
||||
|
||||
if (GLEW_ARB_seamless_cube_map) {
|
||||
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
gl_success("GL_TEXTURE_CUBE_MAP_SEAMLESS");
|
||||
}
|
||||
|
||||
return plat;
|
||||
|
||||
fail:
|
||||
blog(LOG_ERROR, "gl_platform_create failed");
|
||||
gl_platform_destroy(plat);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gs_swap_chain *gl_platform_getswap(struct gl_platform *platform)
|
||||
{
|
||||
if(platform)
|
||||
return &platform->swap;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void gl_platform_destroy(struct gl_platform *platform)
|
||||
{
|
||||
if(!platform)
|
||||
return;
|
||||
|
||||
gl_delete_vertex_arrays(1, &platform->vao);
|
||||
[platform->context release];
|
||||
platform->context = nil;
|
||||
gl_windowinfo_destroy(platform->swap.wi);
|
||||
|
||||
bfree(platform);
|
||||
}
|
||||
|
||||
struct gl_windowinfo *gl_windowinfo_create(struct gs_init_data *info)
|
||||
{
|
||||
if(!info)
|
||||
return NULL;
|
||||
|
||||
if(!info->view)
|
||||
return NULL;
|
||||
|
||||
struct gl_windowinfo *wi = bmalloc(sizeof(struct gl_windowinfo));
|
||||
memset(wi, 0, sizeof(struct gl_windowinfo));
|
||||
|
||||
wi->view = info->view;
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
void gl_windowinfo_destroy(struct gl_windowinfo *wi)
|
||||
{
|
||||
if(!wi)
|
||||
return;
|
||||
|
||||
wi->view = nil;
|
||||
bfree(wi);
|
||||
}
|
||||
|
||||
void device_entercontext(device_t device)
|
||||
{
|
||||
[device->plat->context makeCurrentContext];
|
||||
//gl_bind_vertex_array(device->plat->vao);
|
||||
}
|
||||
|
||||
void device_leavecontext(device_t device)
|
||||
{
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
}
|
||||
|
||||
void device_load_swapchain(device_t device, swapchain_t swap)
|
||||
{
|
||||
if(!swap)
|
||||
swap = &device->plat->swap;
|
||||
|
||||
if(device->cur_swap == swap)
|
||||
return;
|
||||
|
||||
device->cur_swap = swap;
|
||||
}
|
||||
|
||||
void device_present(device_t device)
|
||||
{
|
||||
[device->plat->context flushBuffer];
|
||||
}
|
||||
|
||||
void gl_getclientsize(struct gs_swap_chain *swap, uint32_t *width, uint32_t *height)
|
||||
{
|
||||
if(width) *width = swap->info.cx;
|
||||
if(height) *height = swap->info.cy;
|
||||
}
|
@ -19,6 +19,9 @@
|
||||
|
||||
#include "../util/bmem.h"
|
||||
#include "input.h"
|
||||
#ifdef __APPLE__
|
||||
#include <objc/objc-runtime.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is an API-independent graphics subsystem wrapper.
|
||||
@ -410,7 +413,7 @@ struct gs_init_data {
|
||||
#if defined(_WIN32)
|
||||
void *hwnd;
|
||||
#elif defined(__APPLE__)
|
||||
/* TODO */
|
||||
__unsafe_unretained id view;
|
||||
#elif defined(__posix__)
|
||||
/* TODO */
|
||||
#endif
|
||||
|
61
libobs/obs-cocoa.c
Normal file
61
libobs/obs-cocoa.c
Normal file
@ -0,0 +1,61 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2013 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This program 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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "util/platform.h"
|
||||
#include "util/dstr.h"
|
||||
#include "obs.h"
|
||||
#include "obs-data.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
// support both foo.so and libfoo.so for now
|
||||
static const char *plugin_patterns[] = {
|
||||
"../plugins/%s.so",
|
||||
"../plugins/lib%s.so"
|
||||
};
|
||||
static const int plugin_patterns_size = sizeof(plugin_patterns)/sizeof(plugin_patterns[0]);
|
||||
|
||||
char *find_plugin(const char *plugin)
|
||||
{
|
||||
struct dstr path;
|
||||
dstr_init(&path);
|
||||
for(int i = 0; i < plugin_patterns_size; i++) {
|
||||
dstr_printf(&path, plugin_patterns[i], plugin);
|
||||
if(!access(path.array, F_OK))
|
||||
break;
|
||||
}
|
||||
|
||||
return path.array;
|
||||
}
|
||||
|
||||
/* on windows, points to [base directory]/libobs */
|
||||
char *find_libobs_data_file(const char *file)
|
||||
{
|
||||
struct dstr path;
|
||||
dstr_init_copy(&path, "../libobs/");
|
||||
dstr_cat(&path, file);
|
||||
return path.array;
|
||||
}
|
||||
|
||||
/* on windows, data files should always be in [base directory]/data */
|
||||
char *obs_find_plugin_file(const char *file)
|
||||
{
|
||||
struct dstr path;
|
||||
dstr_init_copy(&path, "../data/");
|
||||
dstr_cat(&path, file);
|
||||
return path.array;
|
||||
}
|
100
libobs/util/platform-cocoa.c
Normal file
100
libobs/util/platform-cocoa.c
Normal file
@ -0,0 +1,100 @@
|
||||
/******************************************************************************
|
||||
Copyright (c) 2013 by Ruwen Hahn <palana@stunned.de>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
******************************************************************************/
|
||||
|
||||
#include "base.h"
|
||||
#include "platform.h"
|
||||
#include "dstr.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
void *os_dlopen(const char *path)
|
||||
{
|
||||
struct dstr dylib_name;
|
||||
dstr_init_copy(&dylib_name, path);
|
||||
if(!dstr_find(&dylib_name, ".so"))
|
||||
dstr_cat(&dylib_name, ".so");
|
||||
|
||||
void *res = dlopen(dylib_name.array, RTLD_LAZY);
|
||||
if(!res)
|
||||
blog(LOG_ERROR, "os_dlopen(%s->%s): %s\n",
|
||||
path, dylib_name.array, dlerror());
|
||||
|
||||
dstr_free(&dylib_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
void *os_dlsym(void *module, const char *func)
|
||||
{
|
||||
return dlsym(module, func);
|
||||
}
|
||||
|
||||
void os_dlclose(void *module)
|
||||
{
|
||||
dlclose(module);
|
||||
}
|
||||
|
||||
void os_sleepto_ns(uint64_t time_target)
|
||||
{
|
||||
uint64_t current = os_gettime_ns();
|
||||
if(time_target < current)
|
||||
return;
|
||||
time_target -= current;
|
||||
struct timespec req,
|
||||
remain;
|
||||
memset(&req, 0, sizeof(req));
|
||||
memset(&remain, 0, sizeof(remain));
|
||||
req.tv_sec = time_target/1000000000;
|
||||
req.tv_nsec = time_target%1000000000;
|
||||
while(nanosleep(&req, &remain))
|
||||
{
|
||||
req = remain;
|
||||
memset(&remain, 0, sizeof(remain));
|
||||
}
|
||||
}
|
||||
|
||||
void os_sleep_ms(uint32_t duration)
|
||||
{
|
||||
usleep(duration*1000);
|
||||
}
|
||||
|
||||
uint64_t os_gettime_ns(void)
|
||||
{
|
||||
uint64_t t = mach_absolute_time();
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
Nanoseconds nano = AbsoluteToNanoseconds(*(AbsoluteTime*) &t);
|
||||
#pragma clang diagnostic pop
|
||||
return *(uint64_t*) &nano;
|
||||
}
|
||||
|
||||
uint64_t os_gettime_ms(void)
|
||||
{
|
||||
return os_gettime_ns()/1000000;
|
||||
}
|
||||
|
193
test/osx/test.mm
Normal file
193
test/osx/test.mm
Normal file
@ -0,0 +1,193 @@
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <OpenGL/OpenGL.h>
|
||||
|
||||
#include "util/base.h"
|
||||
#include "obs.h"
|
||||
|
||||
static const int cx = 800;
|
||||
static const int cy = 600;
|
||||
|
||||
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
using SourceContext = std::unique_ptr<obs_source,
|
||||
std::function<void(obs_source_t)>>;
|
||||
static SourceContext autorelease(obs_source_t s)
|
||||
{
|
||||
return SourceContext(s, obs_source_destroy);
|
||||
}
|
||||
|
||||
using SceneContext = std::unique_ptr<obs_scene,
|
||||
std::function<void(obs_scene_t)>>;
|
||||
static SceneContext autorelease(obs_scene_t s)
|
||||
{
|
||||
return SceneContext(s, obs_scene_destroy);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
static void CreateOBS(NSWindow *win)
|
||||
{
|
||||
struct video_info vi;
|
||||
memset(&vi, 0, sizeof(struct video_info));
|
||||
vi.fps_num = 30000;
|
||||
vi.fps_den = 1001;
|
||||
vi.width = cx;
|
||||
vi.height = cy;
|
||||
vi.name = "video";
|
||||
|
||||
struct gs_init_data gsid;
|
||||
memset(&gsid, 0, sizeof(gsid));
|
||||
gsid.view = [win contentView];
|
||||
gsid.cx = cx;
|
||||
gsid.cy = cy;
|
||||
gsid.num_backbuffers = 2;
|
||||
gsid.format = GS_RGBA;
|
||||
|
||||
if (!obs_startup("libobs-opengl", &gsid, &vi, NULL))
|
||||
throw "Couldn't create OBS";
|
||||
}
|
||||
|
||||
static void AddTestItems(obs_scene_t scene, obs_source_t source)
|
||||
{
|
||||
obs_sceneitem_t item = NULL;
|
||||
struct vec2 v2;
|
||||
|
||||
item = obs_scene_add(scene, source);
|
||||
vec2_set(&v2, 100.0f, 200.0f);
|
||||
obs_sceneitem_setpos(item, &v2);
|
||||
obs_sceneitem_setrot(item, 10.0f);
|
||||
vec2_set(&v2, 20.0f, 2.0f);
|
||||
obs_sceneitem_setscale(item, &v2);
|
||||
|
||||
item = obs_scene_add(scene, source);
|
||||
vec2_set(&v2, 200.0f, 100.0f);
|
||||
obs_sceneitem_setpos(item, &v2);
|
||||
obs_sceneitem_setrot(item, -45.0f);
|
||||
vec2_set(&v2, 5.0f, 7.0f);
|
||||
obs_sceneitem_setscale(item, &v2);
|
||||
}
|
||||
|
||||
@interface window_closer : NSObject {}
|
||||
@end
|
||||
|
||||
@implementation window_closer
|
||||
|
||||
+(id)window_closer
|
||||
{
|
||||
return [[window_closer alloc] init];
|
||||
}
|
||||
|
||||
-(void)windowWillClose:(NSNotification *)notification
|
||||
{
|
||||
[NSApp stop:self];
|
||||
}
|
||||
@end
|
||||
|
||||
static NSWindow *CreateTestWindow()
|
||||
{
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
ProcessSerialNumber psn = {0, kCurrentProcess};
|
||||
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
SetFrontProcess(&psn);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
NSRect content_rect = NSMakeRect(0, 0, cx, cy);
|
||||
NSWindow *win = [[NSWindow alloc]
|
||||
initWithContentRect:content_rect
|
||||
styleMask:NSTitledWindowMask | NSClosableWindowMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
if(!win)
|
||||
return nil;
|
||||
|
||||
[win orderFrontRegardless];
|
||||
|
||||
NSView *view = [[NSView alloc] initWithFrame:content_rect];
|
||||
if(!view)
|
||||
return nil;
|
||||
|
||||
|
||||
[win setContentView:view];
|
||||
[win setTitle:@"foo"];
|
||||
[win center];
|
||||
[win makeMainWindow];
|
||||
[win setDelegate:[window_closer window_closer]];
|
||||
return win;
|
||||
}
|
||||
|
||||
static void test()
|
||||
{
|
||||
try {
|
||||
NSWindow *win = CreateTestWindow();
|
||||
if (!win)
|
||||
throw "Couldn't create main window";
|
||||
|
||||
CreateOBS(win);
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
/* load module */
|
||||
if (obs_load_module("test-input") != 0)
|
||||
throw "Couldn't load module";
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
/* create source */
|
||||
SourceContext source = autorelease(obs_source_create(SOURCE_INPUT,
|
||||
"random", NULL));
|
||||
if (!source)
|
||||
throw "Couldn't create random test source";
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
/* create filter */
|
||||
SourceContext filter = autorelease(obs_source_create(SOURCE_FILTER,
|
||||
"test", NULL));
|
||||
if (!filter)
|
||||
throw "Couldn't create test filter";
|
||||
obs_source_filter_add(source.get(), filter.get());
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
/* create scene and add source to scene (twice) */
|
||||
SceneContext scene = autorelease(obs_scene_create());
|
||||
if (!scene)
|
||||
throw "Couldn't create scene";
|
||||
|
||||
AddTestItems(scene.get(), source.get());
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
/* set the scene as the primary draw source and go */
|
||||
obs_set_primary_source(obs_scene_getsource(scene.get()));
|
||||
|
||||
[NSApp run];
|
||||
|
||||
obs_set_primary_source(NULL);
|
||||
|
||||
} catch (char const *error) {
|
||||
printf("%s\n", error);
|
||||
}
|
||||
|
||||
obs_shutdown();
|
||||
|
||||
blog(LOG_INFO, "Number of memory leaks: %zu", bnum_allocs());
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
int main()
|
||||
{
|
||||
@autoreleasepool {
|
||||
test();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user