Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View File

@ -0,0 +1,165 @@
package tech.kinc
import android.app.NativeActivity
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Message
import android.os.Vibrator
import android.os.VibrationEffect
import android.os.Build
import android.view.KeyEvent
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import kotlin.system.exitProcess
class KincActivity: NativeActivity(), KeyEvent.Callback {
companion object {
var instance: KincActivity? = null
@JvmStatic
fun showKeyboard() {
instance!!.inputManager!!.showSoftInput(instance!!.window.decorView, 0)
}
@JvmStatic
fun hideKeyboard() {
instance!!.inputManager!!.hideSoftInputFromWindow(instance!!.window.decorView.windowToken, 0)
instance!!.delayedHideSystemUI()
}
@JvmStatic
fun loadURL(url: String) {
val i = Intent(Intent.ACTION_VIEW, Uri.parse(url))
instance!!.startActivity(i)
}
@JvmStatic
fun getLanguage(): String {
return java.util.Locale.getDefault().language
}
@JvmStatic
fun vibrate(ms: Int) {
val v: Vibrator = instance!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(ms.toLong(), VibrationEffect.DEFAULT_AMPLITUDE))
}
else {
// deprecated in API 26
v.vibrate(ms.toLong())
}
}
@JvmStatic
fun getRotation(): Int {
val context: Context = instance!!.applicationContext
val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return manager.defaultDisplay.rotation
}
@JvmStatic
fun getScreenDpi(): Int {
val context: Context = instance!!.applicationContext
val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val metrics: android.util.DisplayMetrics = android.util.DisplayMetrics()
manager.defaultDisplay.getMetrics(metrics)
return metrics.xdpi.toInt()
}
@JvmStatic
fun getRefreshRate(): Int {
val context: Context = instance!!.applicationContext
val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return manager.defaultDisplay.refreshRate.toInt()
}
@JvmStatic
fun getDisplayWidth(): Int {
val context: Context = instance!!.applicationContext
val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val size: android.graphics.Point = android.graphics.Point()
manager.defaultDisplay.getRealSize(size)
return size.x
}
@JvmStatic
fun getDisplayHeight(): Int {
val context: Context = instance!!.applicationContext
val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val size: android.graphics.Point = android.graphics.Point()
manager.defaultDisplay.getRealSize(size)
return size.y
}
@JvmStatic
fun stop() {
instance!!.runOnUiThread {
fun run() {
instance!!.finish()
exitProcess(0)
}
}
}
class MyHandler(private val kincActivity: KincActivity) : Handler() {
override fun handleMessage(msg: Message) {
kincActivity.hideSystemUI()
}
}
}
var inputManager: InputMethodManager? = null
private var isDisabledStickyImmersiveMode = false
private val hideSystemUIHandler = MyHandler(this)
override fun onCreate(state: Bundle?) {
super.onCreate(state)
hideSystemUI()
instance = this
inputManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
isDisabledStickyImmersiveMode = try {
val ai: ApplicationInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
val bundle: Bundle = ai.metaData
bundle.getBoolean("disableStickyImmersiveMode")
} catch (e: PackageManager.NameNotFoundException) {
false
} catch (e: NullPointerException) {
false
}
}
private fun hideSystemUI() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
}
private fun delayedHideSystemUI() {
hideSystemUIHandler.removeMessages(0)
if (!isDisabledStickyImmersiveMode) {
hideSystemUIHandler.sendEmptyMessageDelayed(0, 300)
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
delayedHideSystemUI()
}
else {
hideSystemUIHandler.removeMessages(0)
}
}
override fun onKeyMultiple(keyCode: Int, count: Int, event: KeyEvent): Boolean {
this.nativeKincKeyPress(event.characters)
return false
}
private external fun nativeKincKeyPress(chars: String)
}

View File

@ -0,0 +1,50 @@
package tech.kinc
import java.util.ArrayList
import android.view.Surface
class KincMoviePlayer(var path: String) {
companion object {
var players = ArrayList<KincMoviePlayer?>()
@JvmStatic
fun updateAll() {
for (player in KincMoviePlayer.players) {
player!!.update()
}
}
fun remove(id: Int) {
players[id] = null
}
}
private var movieTexture: KincMovieTexture? = null
var id: Int = players.size
init {
players.add(this)
}
fun init() {
movieTexture = KincMovieTexture()
val surface = Surface(movieTexture!!.surfaceTexture)
nativeCreate(path, surface, id)
surface.release()
}
fun getMovieTexture(): KincMovieTexture? {
return movieTexture
}
fun update(): Boolean {
return movieTexture!!.update()
}
fun getTextureId(): Int {
return movieTexture!!.textureId
}
private external fun nativeCreate(path: String, surface: Surface, id: Int)
}

View File

@ -0,0 +1,62 @@
package tech.kinc
import android.graphics.SurfaceTexture
import android.graphics.SurfaceTexture.OnFrameAvailableListener
import android.opengl.GLES20
class KincMovieTexture: OnFrameAvailableListener {
private val GL_TEXTURE_EXTERNAL_OES: Int = 0x8D65
var textureId: Int = 0
init {
val textures = IntArray(1)
GLES20.glGenTextures(1, textures, 0)
textureId = textures[0]
GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId)
GLES20.glTexParameteri(
GL_TEXTURE_EXTERNAL_OES,
GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST
)
GLES20.glTexParameteri(
GL_TEXTURE_EXTERNAL_OES,
GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR
)
GLES20.glTexParameteri(
GL_TEXTURE_EXTERNAL_OES,
GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE
)
GLES20.glTexParameteri(
GL_TEXTURE_EXTERNAL_OES,
GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE
)
}
var surfaceTexture = SurfaceTexture(textureId)
init {
surfaceTexture.setOnFrameAvailableListener(this)
}
private var updateTexture = false
fun update(): Boolean {
val ret = updateTexture
if (updateTexture) {
surfaceTexture.updateTexImage()
updateTexture = false
}
return ret
}
override fun onFrameAvailable(surface: SurfaceTexture) {
if (surfaceTexture == surface) {
updateTexture = true
}
}
}

View File

@ -0,0 +1,441 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <jni.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
#include "android_native_app_glue.h"
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
/* For debug builds, always enable the debug traces in this library */
#ifndef NDEBUG
# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
#else
# define LOGV(...) ((void)0)
#endif
static void free_saved_state(struct android_app* android_app) {
pthread_mutex_lock(&android_app->mutex);
if (android_app->savedState != NULL) {
free(android_app->savedState);
android_app->savedState = NULL;
android_app->savedStateSize = 0;
}
pthread_mutex_unlock(&android_app->mutex);
}
int8_t android_app_read_cmd(struct android_app* android_app) {
int8_t cmd;
if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
switch (cmd) {
case APP_CMD_SAVE_STATE:
free_saved_state(android_app);
break;
}
return cmd;
} else {
LOGE("No data on command pipe!");
}
return -1;
}
static void print_cur_config(struct android_app* android_app) {
char lang[2], country[2];
AConfiguration_getLanguage(android_app->config, lang);
AConfiguration_getCountry(android_app->config, country);
LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
"keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
"modetype=%d modenight=%d",
AConfiguration_getMcc(android_app->config),
AConfiguration_getMnc(android_app->config),
lang[0], lang[1], country[0], country[1],
AConfiguration_getOrientation(android_app->config),
AConfiguration_getTouchscreen(android_app->config),
AConfiguration_getDensity(android_app->config),
AConfiguration_getKeyboard(android_app->config),
AConfiguration_getNavigation(android_app->config),
AConfiguration_getKeysHidden(android_app->config),
AConfiguration_getNavHidden(android_app->config),
AConfiguration_getSdkVersion(android_app->config),
AConfiguration_getScreenSize(android_app->config),
AConfiguration_getScreenLong(android_app->config),
AConfiguration_getUiModeType(android_app->config),
AConfiguration_getUiModeNight(android_app->config));
}
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
switch (cmd) {
case APP_CMD_INPUT_CHANGED:
LOGV("APP_CMD_INPUT_CHANGED\n");
pthread_mutex_lock(&android_app->mutex);
if (android_app->inputQueue != NULL) {
AInputQueue_detachLooper(android_app->inputQueue);
}
android_app->inputQueue = android_app->pendingInputQueue;
if (android_app->inputQueue != NULL) {
LOGV("Attaching input queue to looper");
AInputQueue_attachLooper(android_app->inputQueue,
android_app->looper, LOOPER_ID_INPUT, NULL,
&android_app->inputPollSource);
}
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_INIT_WINDOW:
LOGV("APP_CMD_INIT_WINDOW\n");
pthread_mutex_lock(&android_app->mutex);
android_app->window = android_app->pendingWindow;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_TERM_WINDOW:
LOGV("APP_CMD_TERM_WINDOW\n");
pthread_cond_broadcast(&android_app->cond);
break;
case APP_CMD_RESUME:
case APP_CMD_START:
case APP_CMD_PAUSE:
case APP_CMD_STOP:
LOGV("activityState=%d\n", cmd);
pthread_mutex_lock(&android_app->mutex);
android_app->activityState = cmd;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_CONFIG_CHANGED:
LOGV("APP_CMD_CONFIG_CHANGED\n");
AConfiguration_fromAssetManager(android_app->config,
android_app->activity->assetManager);
print_cur_config(android_app);
break;
case APP_CMD_DESTROY:
LOGV("APP_CMD_DESTROY\n");
android_app->destroyRequested = 1;
break;
}
}
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
switch (cmd) {
case APP_CMD_TERM_WINDOW:
LOGV("APP_CMD_TERM_WINDOW\n");
pthread_mutex_lock(&android_app->mutex);
android_app->window = NULL;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_SAVE_STATE:
LOGV("APP_CMD_SAVE_STATE\n");
pthread_mutex_lock(&android_app->mutex);
android_app->stateSaved = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_RESUME:
free_saved_state(android_app);
break;
}
}
void app_dummy() {
}
static void android_app_destroy(struct android_app* android_app) {
LOGV("android_app_destroy!");
free_saved_state(android_app);
pthread_mutex_lock(&android_app->mutex);
if (android_app->inputQueue != NULL) {
AInputQueue_detachLooper(android_app->inputQueue);
}
AConfiguration_delete(android_app->config);
android_app->destroyed = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
// Can't touch android_app object after this.
}
static void process_input(struct android_app* app, struct android_poll_source* source) {
AInputEvent* event = NULL;
while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
LOGV("New input event: type=%d\n", AInputEvent_getType(event));
if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
continue;
}
int32_t handled = 0;
if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
AInputQueue_finishEvent(app->inputQueue, event, handled);
}
}
static void process_cmd(struct android_app* app, struct android_poll_source* source) {
int8_t cmd = android_app_read_cmd(app);
android_app_pre_exec_cmd(app, cmd);
if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
android_app_post_exec_cmd(app, cmd);
}
static void* android_app_entry(void* param) {
struct android_app* android_app = (struct android_app*)param;
android_app->config = AConfiguration_new();
AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
print_cur_config(android_app);
android_app->cmdPollSource.id = LOOPER_ID_MAIN;
android_app->cmdPollSource.app = android_app;
android_app->cmdPollSource.process = process_cmd;
android_app->inputPollSource.id = LOOPER_ID_INPUT;
android_app->inputPollSource.app = android_app;
android_app->inputPollSource.process = process_input;
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
&android_app->cmdPollSource);
android_app->looper = looper;
pthread_mutex_lock(&android_app->mutex);
android_app->running = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
android_main(android_app);
android_app_destroy(android_app);
return NULL;
}
// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------
static struct android_app* android_app_create(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
memset(android_app, 0, sizeof(struct android_app));
android_app->activity = activity;
pthread_mutex_init(&android_app->mutex, NULL);
pthread_cond_init(&android_app->cond, NULL);
if (savedState != NULL) {
android_app->savedState = malloc(savedStateSize);
android_app->savedStateSize = savedStateSize;
memcpy(android_app->savedState, savedState, savedStateSize);
}
int msgpipe[2];
if (pipe(msgpipe)) {
LOGE("could not create pipe: %s", strerror(errno));
return NULL;
}
android_app->msgread = msgpipe[0];
android_app->msgwrite = msgpipe[1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
// Wait for thread to start.
pthread_mutex_lock(&android_app->mutex);
while (!android_app->running) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
return android_app;
}
static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
}
}
static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
pthread_mutex_lock(&android_app->mutex);
android_app->pendingInputQueue = inputQueue;
android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
while (android_app->inputQueue != android_app->pendingInputQueue) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
pthread_mutex_lock(&android_app->mutex);
if (android_app->pendingWindow != NULL) {
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
}
android_app->pendingWindow = window;
if (window != NULL) {
android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
}
while (android_app->window != android_app->pendingWindow) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, cmd);
while (android_app->activityState != cmd) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
static void android_app_free(struct android_app* android_app) {
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, APP_CMD_DESTROY);
while (!android_app->destroyed) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
close(android_app->msgread);
close(android_app->msgwrite);
pthread_cond_destroy(&android_app->cond);
pthread_mutex_destroy(&android_app->mutex);
free(android_app);
}
static void onDestroy(ANativeActivity* activity) {
LOGV("Destroy: %p\n", activity);
android_app_free((struct android_app*)activity->instance);
}
static void onStart(ANativeActivity* activity) {
LOGV("Start: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
}
static void onResume(ANativeActivity* activity) {
LOGV("Resume: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
}
static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
struct android_app* android_app = (struct android_app*)activity->instance;
void* savedState = NULL;
LOGV("SaveInstanceState: %p\n", activity);
pthread_mutex_lock(&android_app->mutex);
android_app->stateSaved = 0;
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
while (!android_app->stateSaved) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
if (android_app->savedState != NULL) {
savedState = android_app->savedState;
*outLen = android_app->savedStateSize;
android_app->savedState = NULL;
android_app->savedStateSize = 0;
}
pthread_mutex_unlock(&android_app->mutex);
return savedState;
}
static void onPause(ANativeActivity* activity) {
LOGV("Pause: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
}
static void onStop(ANativeActivity* activity) {
LOGV("Stop: %p\n", activity);
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
}
static void onConfigurationChanged(ANativeActivity* activity) {
struct android_app* android_app = (struct android_app*)activity->instance;
LOGV("ConfigurationChanged: %p\n", activity);
android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
}
static void onLowMemory(ANativeActivity* activity) {
struct android_app* android_app = (struct android_app*)activity->instance;
LOGV("LowMemory: %p\n", activity);
android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
}
static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
android_app_write_cmd((struct android_app*)activity->instance,
focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
}
static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
android_app_set_window((struct android_app*)activity->instance, window);
}
static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
android_app_set_window((struct android_app*)activity->instance, NULL);
}
static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
android_app_set_input((struct android_app*)activity->instance, queue);
}
static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
android_app_set_input((struct android_app*)activity->instance, NULL);
}
void ANativeActivity_onCreate(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
LOGV("Creating: %p\n", activity);
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
activity->instance = android_app_create(activity, savedState, savedStateSize);
}

View File

@ -0,0 +1,349 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _ANDROID_NATIVE_APP_GLUE_H
#define _ANDROID_NATIVE_APP_GLUE_H
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <android/configuration.h>
#include <android/looper.h>
#include <android/native_activity.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* The native activity interface provided by <android/native_activity.h>
* is based on a set of application-provided callbacks that will be called
* by the Activity's main thread when certain events occur.
*
* This means that each one of this callbacks _should_ _not_ block, or they
* risk having the system force-close the application. This programming
* model is direct, lightweight, but constraining.
*
* The 'android_native_app_glue' static library is used to provide a different
* execution model where the application can implement its own main event
* loop in a different thread instead. Here's how it works:
*
* 1/ The application must provide a function named "android_main()" that
* will be called when the activity is created, in a new thread that is
* distinct from the activity's main thread.
*
* 2/ android_main() receives a pointer to a valid "android_app" structure
* that contains references to other important objects, e.g. the
* ANativeActivity obejct instance the application is running in.
*
* 3/ the "android_app" object holds an ALooper instance that already
* listens to two important things:
*
* - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
* declarations below.
*
* - input events coming from the AInputQueue attached to the activity.
*
* Each of these correspond to an ALooper identifier returned by
* ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
* respectively.
*
* Your application can use the same ALooper to listen to additional
* file-descriptors. They can either be callback based, or with return
* identifiers starting with LOOPER_ID_USER.
*
* 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
* the returned data will point to an android_poll_source structure. You
* can call the process() function on it, and fill in android_app->onAppCmd
* and android_app->onInputEvent to be called for your own processing
* of the event.
*
* Alternatively, you can call the low-level functions to read and process
* the data directly... look at the process_cmd() and process_input()
* implementations in the glue to see how to do this.
*
* See the sample named "native-activity" that comes with the NDK with a
* full usage example. Also look at the JavaDoc of NativeActivity.
*/
struct android_app;
/**
* Data associated with an ALooper fd that will be returned as the "outData"
* when that source has data ready.
*/
struct android_poll_source {
// The identifier of this source. May be LOOPER_ID_MAIN or
// LOOPER_ID_INPUT.
int32_t id;
// The android_app this ident is associated with.
struct android_app* app;
// Function to call to perform the standard processing of data from
// this source.
void (*process)(struct android_app* app, struct android_poll_source* source);
};
/**
* This is the interface for the standard glue code of a threaded
* application. In this model, the application's code is running
* in its own thread separate from the main thread of the process.
* It is not required that this thread be associated with the Java
* VM, although it will need to be in order to make JNI calls any
* Java objects.
*/
struct android_app {
// The application can place a pointer to its own state object
// here if it likes.
void* userData;
// Fill this in with the function to process main app commands (APP_CMD_*)
void (*onAppCmd)(struct android_app* app, int32_t cmd);
// Fill this in with the function to process input events. At this point
// the event has already been pre-dispatched, and it will be finished upon
// return. Return 1 if you have handled the event, 0 for any default
// dispatching.
int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
// The ANativeActivity object instance that this app is running in.
ANativeActivity* activity;
// The current configuration the app is running in.
AConfiguration* config;
// This is the last instance's saved state, as provided at creation time.
// It is NULL if there was no state. You can use this as you need; the
// memory will remain around until you call android_app_exec_cmd() for
// APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
// These variables should only be changed when processing a APP_CMD_SAVE_STATE,
// at which point they will be initialized to NULL and you can malloc your
// state and place the information here. In that case the memory will be
// freed for you later.
void* savedState;
size_t savedStateSize;
// The ALooper associated with the app's thread.
ALooper* looper;
// When non-NULL, this is the input queue from which the app will
// receive user input events.
AInputQueue* inputQueue;
// When non-NULL, this is the window surface that the app can draw in.
ANativeWindow* window;
// Current content rectangle of the window; this is the area where the
// window's content should be placed to be seen by the user.
ARect contentRect;
// Current state of the app's activity. May be either APP_CMD_START,
// APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
int activityState;
// This is non-zero when the application's NativeActivity is being
// destroyed and waiting for the app thread to complete.
int destroyRequested;
// -------------------------------------------------
// Below are "private" implementation of the glue code.
pthread_mutex_t mutex;
pthread_cond_t cond;
int msgread;
int msgwrite;
pthread_t thread;
struct android_poll_source cmdPollSource;
struct android_poll_source inputPollSource;
int running;
int stateSaved;
int destroyed;
int redrawNeeded;
AInputQueue* pendingInputQueue;
ANativeWindow* pendingWindow;
ARect pendingContentRect;
};
enum {
/**
* Looper data ID of commands coming from the app's main thread, which
* is returned as an identifier from ALooper_pollOnce(). The data for this
* identifier is a pointer to an android_poll_source structure.
* These can be retrieved and processed with android_app_read_cmd()
* and android_app_exec_cmd().
*/
LOOPER_ID_MAIN = 1,
/**
* Looper data ID of events coming from the AInputQueue of the
* application's window, which is returned as an identifier from
* ALooper_pollOnce(). The data for this identifier is a pointer to an
* android_poll_source structure. These can be read via the inputQueue
* object of android_app.
*/
LOOPER_ID_INPUT = 2,
/**
* Start of user-defined ALooper identifiers.
*/
LOOPER_ID_USER = 3,
};
enum {
/**
* Command from main thread: the AInputQueue has changed. Upon processing
* this command, android_app->inputQueue will be updated to the new queue
* (or NULL).
*/
APP_CMD_INPUT_CHANGED,
/**
* Command from main thread: a new ANativeWindow is ready for use. Upon
* receiving this command, android_app->window will contain the new window
* surface.
*/
APP_CMD_INIT_WINDOW,
/**
* Command from main thread: the existing ANativeWindow needs to be
* terminated. Upon receiving this command, android_app->window still
* contains the existing window; after calling android_app_exec_cmd
* it will be set to NULL.
*/
APP_CMD_TERM_WINDOW,
/**
* Command from main thread: the current ANativeWindow has been resized.
* Please redraw with its new size.
*/
APP_CMD_WINDOW_RESIZED,
/**
* Command from main thread: the system needs that the current ANativeWindow
* be redrawn. You should redraw the window before handing this to
* android_app_exec_cmd() in order to avoid transient drawing glitches.
*/
APP_CMD_WINDOW_REDRAW_NEEDED,
/**
* Command from main thread: the content area of the window has changed,
* such as from the soft input window being shown or hidden. You can
* find the new content rect in android_app::contentRect.
*/
APP_CMD_CONTENT_RECT_CHANGED,
/**
* Command from main thread: the app's activity window has gained
* input focus.
*/
APP_CMD_GAINED_FOCUS,
/**
* Command from main thread: the app's activity window has lost
* input focus.
*/
APP_CMD_LOST_FOCUS,
/**
* Command from main thread: the current device configuration has changed.
*/
APP_CMD_CONFIG_CHANGED,
/**
* Command from main thread: the system is running low on memory.
* Try to reduce your memory use.
*/
APP_CMD_LOW_MEMORY,
/**
* Command from main thread: the app's activity has been started.
*/
APP_CMD_START,
/**
* Command from main thread: the app's activity has been resumed.
*/
APP_CMD_RESUME,
/**
* Command from main thread: the app should generate a new saved state
* for itself, to restore from later if needed. If you have saved state,
* allocate it with malloc and place it in android_app.savedState with
* the size in android_app.savedStateSize. The will be freed for you
* later.
*/
APP_CMD_SAVE_STATE,
/**
* Command from main thread: the app's activity has been paused.
*/
APP_CMD_PAUSE,
/**
* Command from main thread: the app's activity has been stopped.
*/
APP_CMD_STOP,
/**
* Command from main thread: the app's activity is being destroyed,
* and waiting for the app thread to clean up and exit before proceeding.
*/
APP_CMD_DESTROY,
};
/**
* Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
* app command message.
*/
int8_t android_app_read_cmd(struct android_app* android_app);
/**
* Call with the command returned by android_app_read_cmd() to do the
* initial pre-processing of the given command. You can perform your own
* actions for the command after calling this function.
*/
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
/**
* Call with the command returned by android_app_read_cmd() to do the
* final post-processing of the given command. You must have done your own
* actions for the command before calling this function.
*/
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
/**
* Dummy function you can call to ensure glue code isn't stripped.
*/
void app_dummy();
/**
* This is the function that application code must implement, representing
* the main entry to the app.
*/
extern void android_main(struct android_app* app);
#ifdef __cplusplus
}
#endif
#endif /* _ANDROID_NATIVE_APP_GLUE_H */

View File

@ -0,0 +1,18 @@
#pragma once
#include <android_native_app_glue.h>
#ifdef __cplusplus
extern "C" {
#endif
// name in usual Java syntax (points, no slashes)
jclass kinc_android_find_class(JNIEnv *env, const char *name);
ANativeActivity *kinc_android_get_activity(void);
AAssetManager *kinc_android_get_asset_manager(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,385 @@
#if 0
#include "VrInterface.h"
#ifdef VR_GEAR_VR
#include <kha/Image.h>
#include <kha/math/Matrix4.h>
#include <kha/math/Quaternion.h>
#include <kha/math/Vector3.h>
#include <kha/vr/Pose.h>
#include <kha/vr/PoseState.h>
#include <kha/vr/TimeWarpImage.h>
#include <GlTexture.h>
#include <VrApi/VrApi.h>
#include <VrApi/VrApi_Helpers.h>
#include <LibOvr/Src/Kernel/OVR_Math.h>
#include <Kore/log.h>
#endif
namespace Kore {
//
namespace VrInterface {
// // Is Set during Initialize
#ifdef VR_GEAR_VR
static ovrMobile* ovr;
#endif
static JavaVM* cachedJVM;
static jobject instance;
static jclass koreActivity;
static float qx;
static float qy;
static float qz;
static float qw;
//
void SetJVM(JavaVM* jvm) {
cachedJVM = jvm;
// Grab the activity object
JNIEnv* env;
cachedJVM->AttachCurrentThread(&env, 0);
koreActivity = env->FindClass("tech/kode/kore/KoreActivity");
koreActivity = (jclass) env->NewGlobalRef(koreActivity);
jmethodID mid = env->GetStaticMethodID(koreActivity, "getInstance", "()Ltech/kode/kore/KoreActivity;");
instance = env->CallStaticObjectMethod(koreActivity, mid);
// Make sure that the garbage collector does not clean this up for us
instance = env->NewGlobalRef(instance);
}
#ifdef VR_CARDBOARD
void DistortionBefore() {
JNIEnv* env;
cachedJVM->AttachCurrentThread(&env, 0);
jmethodID mid = env->GetMethodID(koreActivity, "DistortionBeforeFrame", "()V");
env->CallObjectMethod(instance, mid);
}
void DistortionAfter() {
JNIEnv* env;
cachedJVM->AttachCurrentThread(&env, 0);
jmethodID mid = env->GetMethodID(koreActivity, "DistortionAfterFrame", "()V");
env->CallObjectMethod(instance, mid);
}
void DistortTexture(kha::Image_obj* image) {
JNIEnv* env;
cachedJVM->AttachCurrentThread(&env, 0);
jmethodID mid = env->GetMethodID(koreActivity, "DistortTexture", "(I)V");
env->CallVoidMethod(instance, mid, image->renderTarget->_texture);
}
void updateGaze(float x, float y, float z, float w) {
qx = x;
qy = y;
qz = z;
qw = w;
}
template<typename T> T* CreateEmpty() {
return dynamic_cast<T*>(T::__CreateEmpty().mPtr);
}
kha::math::Quaternion_obj* getGaze() {
kha::math::Quaternion_obj* result = CreateEmpty<kha::math::Quaternion_obj>();
result->__construct(qx, qy, qz, qw);
return result;
}
#endif
#ifdef VR_GEAR_VR
void Initialize() {
ovrModeParms parms;
parms.AsynchronousTimeWarp = true;
parms.AllowPowerSave = true;
parms.DistortionFileName = 0;
parms.EnableImageServer = false;
parms.SkipWindowFullscreenReset = true;
// Grab the activity object
JNIEnv* env;
cachedJVM->AttachCurrentThread(&env, 0);
jclass koreActivity = env->FindClass("tech/kode/kore/KoreActivity");
jmethodID mid = env->GetStaticMethodID(koreActivity, "getInstance", "()Ltech/kode/kore/KoreActivity;");
jobject instance = env->CallStaticObjectMethod(koreActivity, mid);
// Make sure that the garbage collector does not clean this up for us
instance = env->NewGlobalRef(instance);
parms.ActivityObject = instance;
parms.GameThreadTid = 0;
parms.CpuLevel = 2;
parms.GpuLevel = 2;
ovrHmdInfo returnedHmdInfo;
ovr = ovr_EnterVrMode(parms, &returnedHmdInfo );
}
void WarpSwapBlack() {
// TODO: Not in the API anymore :-(
//ovr_WarpSwapBlack(ovr);
}
void WarpSwapLoadingIcon() {
//ovr_WarpSwapLoadingIcon(ovr);
}
template<typename T> T* CreateEmpty() {
return dynamic_cast<T*>(T::__CreateEmpty().mPtr);
}
kha::math::Quaternion_obj* GetQuaternion(const ovrQuatf& q) {
kha::math::Quaternion_obj* quaternion = CreateEmpty<kha::math::Quaternion_obj>();
quaternion->__construct(0.0f, 0.0f, 0.0f, 0.0f);
quaternion->set_x(q.x);
quaternion->set_y(q.y);
quaternion->set_z(q.z);
quaternion->set_w(q.w);
return quaternion;
}
ovrQuatf GetQuaternion(kha::math::Quaternion_obj* quat) {
ovrQuatf result;
result.x = quat->get_x();
result.y = quat->get_y();
result.z = quat->get_z();
result.w = quat->get_w();
return result;
}
ovrMatrix4f GetMatrix(kha::math::Matrix4_obj* mat) {
ovrMatrix4f result;
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
float f = mat->get(x, y);
result.M[x][y] = f;
}
}
return result;
}
kha::math::Vector3_obj* GetVector3(const ovrVector3f& v) {
kha::math::Vector3_obj* vector = CreateEmpty<kha::math::Vector3_obj>();
vector->x = v.x;
vector->y = v.y;
vector->z = v.z;
return vector;
}
ovrVector3f GetVector3(kha::math::Vector3_obj* v) {
ovrVector3f result;
result.x = v->x;
result.y = v->y;
result.z = v->z;
return result;
}
kha::vr::Pose_obj* GetPose(const ovrPosef& nativePose) {
kha::vr::Pose_obj* pose = CreateEmpty<kha::vr::Pose_obj>();
pose->Position = GetVector3(nativePose.Position);
pose->Orientation = GetQuaternion(nativePose.Orientation);
return pose;
}
kha::vr::PoseState_obj* GetPoseState(const ovrPoseStatef& nativeState) {
kha::vr::PoseState_obj* poseState = CreateEmpty<kha::vr::PoseState_obj>();
poseState->TimeInSeconds = nativeState.TimeInSeconds;
poseState->AngularAcceleration = GetVector3(nativeState.AngularAcceleration);
poseState->AngularVelocity = GetVector3(nativeState.AngularVelocity);
poseState->LinearAcceleration = GetVector3(nativeState.LinearAcceleration);
poseState->LinearVelocity = GetVector3(nativeState.LinearVelocity);
poseState->Pose = GetPose(nativeState.Pose);
return poseState;
}
kha::vr::SensorState_obj* GetPredictedSensorState(const float time) {
kha::vr::SensorState_obj* state = dynamic_cast<kha::vr::SensorState_obj*>(kha::vr::SensorState_obj::__CreateEmpty().mPtr);
ovrSensorState nativeState = ovr_GetPredictedSensorState(ovr, time);
state->Temperature = nativeState.Temperature;
state->Status = nativeState.Status;
state->Predicted = GetPoseState(nativeState.Predicted);
state->Recorded = GetPoseState(nativeState.Recorded);
return state;
}
kha::vr::SensorState_obj* GetSensorState() {
// 0.0 gets the last reading
return GetPredictedSensorState(0.0f);
}
ovrPosef GetPose(kha::vr::Pose_obj* pose) {
ovrPosef result;
result.Orientation = GetQuaternion(pose->Orientation.mPtr);
result.Position = GetVector3(pose->Position.mPtr);
return result;
}
ovrPoseStatef GetPoseState(kha::vr::PoseState_obj* poseState) {
ovrPoseStatef result;
result.TimeInSeconds = poseState->TimeInSeconds;
result.AngularAcceleration = GetVector3(poseState->AngularAcceleration.mPtr);
result.AngularVelocity = GetVector3(poseState->AngularVelocity.mPtr);
result.LinearAcceleration = GetVector3(poseState->LinearAcceleration.mPtr);
result.LinearVelocity = GetVector3(poseState->LinearVelocity.mPtr);
result.Pose = GetPose(poseState->Pose.mPtr);
return result;
}
ovrTimeWarpImage GetTimeWarpImage(kha::vr::TimeWarpImage_obj* image) {
ovrTimeWarpImage result;
if (image == 0) {
result.TexId = 0;
return result;
}
if (image->Image->renderTarget != 0) {
result.TexId = image->Image->renderTarget->_texture;
} else {
result.TexId = image->Image->texture->texture;
}
result.Pose = GetPoseState(image->Pose.mPtr);
result.TexCoordsFromTanAngles = GetMatrix(image->TexCoordsFromTanAngles.mPtr);
result.TexCoordsFromTanAngles = //TanAngleMatrixFromProjection(&result.TexCoordsFromTanAngles);
TanAngleMatrixFromFov(90.0f);
return result;
}
bool AreDifferent(ovrMatrix4f& lhs, ovrMatrix4f& rhs) {
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
if (Kore::abs(lhs.M[x][y] - rhs.M[x][y]) > 0.1f) return true;
}
}
return false;
}
void WarpSwap(kha::vr::TimeWarpParms_obj* parms) {
ovrTimeWarpParms nativeParms = InitTimeWarpParms();
const double predictedTime = ovr_GetPredictedDisplayTime( ovr, 1, 1 );
const ovrSensorState state = ovr_GetPredictedSensorState( ovr, predictedTime );
ovrTimeWarpImage leftImage = GetTimeWarpImage(parms->LeftImage.mPtr);
ovrTimeWarpImage rightImage = GetTimeWarpImage(parms->RightImage.mPtr);
ovrTimeWarpImage leftOverlay = GetTimeWarpImage(parms->LeftOverlay.mPtr);
ovrTimeWarpImage rightOverlay = GetTimeWarpImage(parms->RightOverlay.mPtr);
leftImage.Pose = state.Predicted;
leftOverlay.TexId = 0;
rightOverlay.TexId = 0;
//nativeParms->WarpProgram = WP_SIMPLE;
nativeParms.Images[0][0] = leftImage;
nativeParms.Images[0][1] = leftOverlay;
nativeParms.Images[1][0] = rightImage;
nativeParms.Images[1][1] = rightOverlay;
// nativeParms->WarpProgram = WP_OVERLAY_PLANE;
/*ovrMatrix4f comparison = OVR::Matrix4f::Translation(1.0f, 2.0f, 3.0f);
if (AreDifferent(comparison, nativeParms->Images[0][0].TexCoordsFromTanAngles)) {
Kore::log(Kore::Info, "Matrices are different!");
} else {
Kore::log(Kore::Info, "Matrices are identical");
} */
//ovrTimeWarpParms testParms = InitTimeWarpParms( WARP_INIT_LOADING_ICON);
ovr_WarpSwap(ovr, &nativeParms);
// TODO: What about memory - who deletes What?
}
double GetTimeInSeconds() {
return ovr_GetTimeInSeconds();
}
#endif
}
//
}
#endif

View File

@ -0,0 +1,55 @@
#pragma once
#ifdef ANDROID
#include <jni.h>
#endif
#include <kha/vr/SensorState.h>
#include <kha/vr/TimeWarpParms.h>
#include <kha/Image.h>
#include <kha/math/Quaternion.h>
namespace Kore {
namespace VrInterface {
#ifdef ANDROID
// Save the JVM. Must be called before Initialize().
// TODO: Can this be handled better?
void SetJVM(JavaVM *jvm);
#endif
#ifdef VR_CARDBOARD
void DistortionBefore();
void DistortionAfter();
void DistortTexture(kha::Image_obj *image);
void updateGaze(float x, float y, float z, float w);
kha::math::Quaternion_obj *getGaze();
#endif
#ifdef VR_GEAR_VR
// Calls ovr_enterVrMode
void Initialize();
void WarpSwapBlack();
void WarpSwapLoadingIcon();
kha::vr::SensorState_obj *GetSensorState();
kha::vr::SensorState_obj *GetPredictedSensorState(float time);
double GetTimeInSeconds();
void WarpSwap(kha::vr::TimeWarpParms_obj *parms);
#endif
}
}

View File

@ -0,0 +1,5 @@
#include "audio.c.h"
#include "display.c.h"
#include "system.c.h"
#include "window.c.h"
#include "video.c.h"

View File

@ -0,0 +1,133 @@
#include <kinc/audio2/audio.h>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <stdlib.h>
#include <string.h>
static kinc_a2_buffer_t a2_buffer;
static SLObjectItf engineObject;
static SLEngineItf engineEngine;
static SLObjectItf outputMixObject;
static SLObjectItf bqPlayerObject;
static SLPlayItf bqPlayerPlay = NULL;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
#define AUDIO_BUFFER_SIZE 1 * 1024
static int16_t tempBuffer[AUDIO_BUFFER_SIZE];
static void copySample(void *buffer) {
float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location];
float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location];
a2_buffer.read_location += 1;
if (a2_buffer.read_location >= a2_buffer.data_size) {
a2_buffer.read_location = 0;
}
((int16_t *)buffer)[0] = (int16_t)(left_value * 32767);
((int16_t *)buffer)[1] = (int16_t)(right_value * 32767);
}
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf caller, void *context) {
if (kinc_a2_internal_callback(&a2_buffer, AUDIO_BUFFER_SIZE / 2)) {
for (int i = 0; i < AUDIO_BUFFER_SIZE; i += 2) {
copySample(&tempBuffer[i]);
}
}
else {
memset(tempBuffer, 0, sizeof(tempBuffer));
}
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, tempBuffer, AUDIO_BUFFER_SIZE * 2);
}
static bool initialized = false;
void kinc_a2_init() {
if (initialized) {
return;
}
kinc_a2_internal_init();
initialized = true;
a2_buffer.read_location = 0;
a2_buffer.write_location = 0;
a2_buffer.data_size = 128 * 1024;
a2_buffer.channel_count = 2;
a2_buffer.channels[0] = (float*)malloc(a2_buffer.data_size * sizeof(float));
a2_buffer.channels[1] = (float*)malloc(a2_buffer.data_size * sizeof(float));
SLresult result;
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
const SLInterfaceID ids[] = {SL_IID_VOLUME};
const SLboolean req[] = {SL_BOOLEAN_FALSE};
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 2,
SL_SAMPLINGRATE_44_1, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
SL_BYTEORDER_LITTLEENDIAN};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&loc_outmix, NULL};
const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req1[] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(bqPlayerObject), &audioSrc, &audioSnk, 1, ids1, req1);
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &(bqPlayerPlay));
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(bqPlayerBufferQueue));
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
memset(tempBuffer, 0, sizeof(tempBuffer));
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, tempBuffer, AUDIO_BUFFER_SIZE * 2);
}
void pauseAudio() {
if (bqPlayerPlay == NULL) {
return;
}
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
}
void resumeAudio() {
if (bqPlayerPlay == NULL) {
return;
}
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
}
void kinc_a2_update() {}
void kinc_a2_shutdown() {
if (bqPlayerObject != NULL) {
(*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = NULL;
bqPlayerPlay = NULL;
bqPlayerBufferQueue = NULL;
}
if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL;
}
if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject);
engineObject = NULL;
engineEngine = NULL;
}
}
uint32_t kinc_a2_samples_per_second(void) {
return 44100;
}

View File

@ -0,0 +1,106 @@
#include <kinc/backend/Android.h>
#include <kinc/display.h>
#include <kinc/log.h>
typedef struct {
bool available;
int x;
int y;
int width;
int height;
bool primary;
int number;
} kinc_display_t;
static kinc_display_t display;
int kinc_count_displays(void) {
return 1;
}
int kinc_primary_display(void) {
return 0;
}
static int width() {
JNIEnv *env;
JavaVM *vm = kinc_android_get_activity()->vm;
(*vm)->AttachCurrentThread(vm, &env, NULL);
jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getDisplayWidth", "()I");
int width = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi);
(*vm)->DetachCurrentThread(vm);
return width;
}
static int height() {
JNIEnv *env;
JavaVM *vm = kinc_android_get_activity()->vm;
(*vm)->AttachCurrentThread(vm, &env, NULL);
jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getDisplayHeight", "()I");
int height = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi);
(*vm)->DetachCurrentThread(vm);
return height;
}
static int pixelsPerInch() {
JNIEnv *env;
JavaVM *vm = kinc_android_get_activity()->vm;
(*vm)->AttachCurrentThread(vm, &env, NULL);
jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getScreenDpi", "()I");
int dpi = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi);
(*vm)->DetachCurrentThread(vm);
return dpi;
}
static int refreshRate() {
JNIEnv *env;
JavaVM *vm = kinc_android_get_activity()->vm;
(*vm)->AttachCurrentThread(vm, &env, NULL);
jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity");
jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getRefreshRate", "()I");
int dpi = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi);
(*vm)->DetachCurrentThread(vm);
return dpi;
}
void kinc_display_init() {}
kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index) {
kinc_display_mode_t mode;
mode.x = 0;
mode.y = 0;
mode.width = width();
mode.height = height();
mode.frequency = refreshRate();
mode.bits_per_pixel = 32;
mode.pixels_per_inch = pixelsPerInch();
return mode;
}
int kinc_display_count_available_modes(int display_index) {
return 1;
}
kinc_display_mode_t kinc_display_current_mode(int display) {
kinc_display_mode_t mode;
mode.x = 0;
mode.y = 0;
mode.width = width();
mode.height = height();
mode.frequency = refreshRate();
mode.bits_per_pixel = 32;
mode.pixels_per_inch = pixelsPerInch();
return mode;
}
const char *kinc_display_name(int display) {
return "Display";
}
bool kinc_display_available(int display) {
return display == 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,581 @@
#include <kinc/video.h>
#include <kinc/audio1/audio.h>
#include <kinc/graphics4/texture.h>
#include <kinc/io/filereader.h>
#include <kinc/log.h>
#include <kinc/system.h>
#include <android_native_app_glue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
#include <OMXAL/OpenMAXAL.h>
#include <OMXAL/OpenMAXAL_Android.h>
#endif
#include <assert.h>
#include <jni.h>
#include <kinc/backend/Android.h>
#include <pthread.h>
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/native_window_jni.h>
#endif
void kinc_video_sound_stream_impl_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {
stream->bufferSize = 1;
stream->bufferReadPosition = 0;
stream->bufferWritePosition = 0;
stream->read = 0;
stream->written = 0;
}
void kinc_video_sound_stream_impl_destroy(kinc_internal_video_sound_stream_t *stream) {}
void kinc_video_sound_stream_impl_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {}
static float samples[2] = {0};
float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) {
return samples;
}
bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) {
return false;
}
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
#define videosCount 10
static kinc_video_t *videos[videosCount] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
#define NB_MAXAL_INTERFACES 3 // XAAndroidBufferQueueItf, XAStreamInformationItf and XAPlayItf
#define NB_BUFFERS 8
#define MPEG2_TS_PACKET_SIZE 188
#define PACKETS_PER_BUFFER 10
#define BUFFER_SIZE (PACKETS_PER_BUFFER * MPEG2_TS_PACKET_SIZE)
static const int kEosBufferCntxt = 1980; // a magic value we can compare against
typedef struct kinc_android_video {
XAObjectItf engineObject;
XAEngineItf engineEngine;
XAObjectItf outputMixObject;
const char *path;
AAsset *file;
XAObjectItf playerObj;
XAPlayItf playerPlayItf;
XAAndroidBufferQueueItf playerBQItf;
XAStreamInformationItf playerStreamInfoItf;
XAVolumeItf playerVolItf;
char dataCache[BUFFER_SIZE * NB_BUFFERS];
ANativeWindow *theNativeWindow;
jboolean reachedEof;
pthread_mutex_t mutex;
pthread_cond_t cond;
bool discontinuity;
} kinc_android_video_t;
void kinc_android_video_init(kinc_android_video_t *video) {
video->engineObject = NULL;
video->engineEngine = NULL;
video->outputMixObject = NULL;
video->file = NULL;
video->playerObj = NULL;
video->playerPlayItf = NULL;
video->playerBQItf = NULL;
video->playerStreamInfoItf = NULL;
video->playerVolItf = NULL;
video->theNativeWindow = NULL;
video->reachedEof = JNI_FALSE;
memset(&video->mutex, 0, sizeof(video->mutex)); // mutex = PTHREAD_MUTEX_INITIALIZER; // simple assign stopped working in Android Studio 2.2
memset(&video->cond, 0, sizeof(video->cond)); // cond = PTHREAD_COND_INITIALIZER; // simple assign stopped working in Android Studio 2.2
video->discontinuity = false;
}
bool kinc_android_video_enqueue_initial_buffers(kinc_android_video_t *video, bool discontinuity) {
// Fill our cache.
// We want to read whole packets (integral multiples of MPEG2_TS_PACKET_SIZE).
// fread returns units of "elements" not bytes, so we ask for 1-byte elements
// and then check that the number of elements is a multiple of the packet size.
//
size_t bytesRead;
// bytesRead = fread(dataCache, 1, BUFFER_SIZE * NB_BUFFERS, file);
bytesRead = AAsset_read(video->file, video->dataCache, BUFFER_SIZE * NB_BUFFERS);
if (bytesRead <= 0) {
// could be premature EOF or I/O error
return false;
}
if ((bytesRead % MPEG2_TS_PACKET_SIZE) != 0) {
kinc_log(KINC_LOG_LEVEL_INFO, "Dropping last packet because it is not whole");
}
size_t packetsRead = bytesRead / MPEG2_TS_PACKET_SIZE;
kinc_log(KINC_LOG_LEVEL_INFO, "Initially queueing %zu packets", packetsRead);
// Enqueue the content of our cache before starting to play,
// we don't want to starve the player
size_t i;
for (i = 0; i < NB_BUFFERS && packetsRead > 0; i++) {
// compute size of this buffer
size_t packetsThisBuffer = packetsRead;
if (packetsThisBuffer > PACKETS_PER_BUFFER) {
packetsThisBuffer = PACKETS_PER_BUFFER;
}
size_t bufferSize = packetsThisBuffer * MPEG2_TS_PACKET_SIZE;
XAresult res;
if (discontinuity) {
// signal discontinuity
XAAndroidBufferItem items[1];
items[0].itemKey = XA_ANDROID_ITEMKEY_DISCONTINUITY;
items[0].itemSize = 0;
// DISCONTINUITY message has no parameters,
// so the total size of the message is the size of the key
// plus the size if itemSize, both XAuint32
res = (*video->playerBQItf)
->Enqueue(video->playerBQItf, NULL /*pBufferContext*/, video->dataCache + i * BUFFER_SIZE, bufferSize, items /*pMsg*/,
sizeof(XAuint32) * 2 /*msgLength*/);
discontinuity = JNI_FALSE;
}
else {
res = (*video->playerBQItf)->Enqueue(video->playerBQItf, NULL /*pBufferContext*/, video->dataCache + i * BUFFER_SIZE, bufferSize, NULL, 0);
}
assert(XA_RESULT_SUCCESS == res);
packetsRead -= packetsThisBuffer;
}
return true;
}
static XAresult AndroidBufferQueueCallback(XAAndroidBufferQueueItf caller, void *pCallbackContext, /* input */
void *pBufferContext, /* input */
void *pBufferData, /* input */
XAuint32 dataSize, /* input */
XAuint32 dataUsed, /* input */
const XAAndroidBufferItem *pItems, /* input */
XAuint32 itemsLength /* input */) {
kinc_android_video_t *self = (kinc_android_video_t *)pCallbackContext;
XAresult res;
int ok;
// pCallbackContext was specified as NULL at RegisterCallback and is unused here
// assert(NULL == pCallbackContext);
// note there is never any contention on this mutex unless a discontinuity request is active
ok = pthread_mutex_lock(&self->mutex);
assert(0 == ok);
// was a discontinuity requested?
if (self->discontinuity) {
// Note: can't rewind after EOS, which we send when reaching EOF
// (don't send EOS if you plan to play more content through the same player)
if (!self->reachedEof) {
// clear the buffer queue
res = (*self->playerBQItf)->Clear(self->playerBQItf);
assert(XA_RESULT_SUCCESS == res);
// rewind the data source so we are guaranteed to be at an appropriate point
// rewind(file);
AAsset_seek(self->file, 0, SEEK_SET);
// Enqueue the initial buffers, with a discontinuity indicator on first buffer
kinc_android_video_enqueue_initial_buffers(self, JNI_TRUE);
}
// acknowledge the discontinuity request
self->discontinuity = JNI_FALSE;
ok = pthread_cond_signal(&self->cond);
assert(0 == ok);
goto exit;
}
if ((pBufferData == NULL) && (pBufferContext != NULL)) {
const int processedCommand = *(int *)pBufferContext;
if (kEosBufferCntxt == processedCommand) {
kinc_log(KINC_LOG_LEVEL_INFO, "EOS was processed");
// our buffer with the EOS message has been consumed
assert(0 == dataSize);
goto exit;
}
}
// pBufferData is a pointer to a buffer that we previously Enqueued
assert((dataSize > 0) && ((dataSize % MPEG2_TS_PACKET_SIZE) == 0));
assert(self->dataCache <= (char *)pBufferData && (char *)pBufferData < &self->dataCache[BUFFER_SIZE * NB_BUFFERS]);
assert(0 == (((char *)pBufferData - self->dataCache) % BUFFER_SIZE));
// don't bother trying to read more data once we've hit EOF
if (self->reachedEof) {
goto exit;
}
size_t nbRead;
// note we do call fread from multiple threads, but never concurrently
size_t bytesRead;
// bytesRead = fread(pBufferData, 1, BUFFER_SIZE, file);
bytesRead = AAsset_read(self->file, pBufferData, BUFFER_SIZE);
if (bytesRead > 0) {
if ((bytesRead % MPEG2_TS_PACKET_SIZE) != 0) {
kinc_log(KINC_LOG_LEVEL_INFO, "Dropping last packet because it is not whole");
}
size_t packetsRead = bytesRead / MPEG2_TS_PACKET_SIZE;
size_t bufferSize = packetsRead * MPEG2_TS_PACKET_SIZE;
res = (*caller)->Enqueue(caller, NULL /*pBufferContext*/, pBufferData /*pData*/, bufferSize /*dataLength*/, NULL /*pMsg*/, 0 /*msgLength*/);
assert(XA_RESULT_SUCCESS == res);
}
else {
// EOF or I/O error, signal EOS
XAAndroidBufferItem msgEos[1];
msgEos[0].itemKey = XA_ANDROID_ITEMKEY_EOS;
msgEos[0].itemSize = 0;
// EOS message has no parameters, so the total size of the message is the size of the key
// plus the size if itemSize, both XAuint32
res = (*caller)->Enqueue(caller, (void *)&kEosBufferCntxt /*pBufferContext*/, NULL /*pData*/, 0 /*dataLength*/, msgEos /*pMsg*/,
sizeof(XAuint32) * 2 /*msgLength*/);
assert(XA_RESULT_SUCCESS == res);
self->reachedEof = JNI_TRUE;
}
exit:
ok = pthread_mutex_unlock(&self->mutex);
assert(0 == ok);
return XA_RESULT_SUCCESS;
}
static void StreamChangeCallback(XAStreamInformationItf caller, XAuint32 eventId, XAuint32 streamIndex, void *pEventData, void *pContext) {
kinc_log(KINC_LOG_LEVEL_INFO, "StreamChangeCallback called for stream %u", streamIndex);
kinc_android_video_t *self = (kinc_android_video_t *)pContext;
// pContext was specified as NULL at RegisterStreamChangeCallback and is unused here
// assert(NULL == pContext);
switch (eventId) {
case XA_STREAMCBEVENT_PROPERTYCHANGE: {
// From spec 1.0.1:
// "This event indicates that stream property change has occurred.
// The streamIndex parameter identifies the stream with the property change.
// The pEventData parameter for this event is not used and shall be ignored."
//
XAresult res;
XAuint32 domain;
res = (*caller)->QueryStreamType(caller, streamIndex, &domain);
assert(XA_RESULT_SUCCESS == res);
switch (domain) {
case XA_DOMAINTYPE_VIDEO: {
XAVideoStreamInformation videoInfo;
res = (*caller)->QueryStreamInformation(caller, streamIndex, &videoInfo);
assert(XA_RESULT_SUCCESS == res);
kinc_log(KINC_LOG_LEVEL_INFO, "Found video size %u x %u, codec ID=%u, frameRate=%u, bitRate=%u, duration=%u ms", videoInfo.width, videoInfo.height,
videoInfo.codecId, videoInfo.frameRate, videoInfo.bitRate, videoInfo.duration);
} break;
default:
kinc_log(KINC_LOG_LEVEL_ERROR, "Unexpected domain %u\n", domain);
break;
}
} break;
default:
kinc_log(KINC_LOG_LEVEL_ERROR, "Unexpected stream event ID %u\n", eventId);
break;
}
}
bool kinc_android_video_open(kinc_android_video_t *video, const char *filename) {
XAresult res;
// create engine
res = xaCreateEngine(&video->engineObject, 0, NULL, 0, NULL, NULL);
assert(XA_RESULT_SUCCESS == res);
// realize the engine
res = (*video->engineObject)->Realize(video->engineObject, XA_BOOLEAN_FALSE);
assert(XA_RESULT_SUCCESS == res);
// get the engine interface, which is needed in order to create other objects
res = (*video->engineObject)->GetInterface(video->engineObject, XA_IID_ENGINE, &video->engineEngine);
assert(XA_RESULT_SUCCESS == res);
// create output mix
res = (*video->engineEngine)->CreateOutputMix(video->engineEngine, &video->outputMixObject, 0, NULL, NULL);
assert(XA_RESULT_SUCCESS == res);
// realize the output mix
res = (*video->outputMixObject)->Realize(video->outputMixObject, XA_BOOLEAN_FALSE);
assert(XA_RESULT_SUCCESS == res);
// open the file to play
video->file = AAssetManager_open(kinc_android_get_asset_manager(), filename, AASSET_MODE_STREAMING);
if (video->file == NULL) {
kinc_log(KINC_LOG_LEVEL_INFO, "Could not find video file.");
return false;
}
// configure data source
XADataLocator_AndroidBufferQueue loc_abq = {XA_DATALOCATOR_ANDROIDBUFFERQUEUE, NB_BUFFERS};
XADataFormat_MIME format_mime = {XA_DATAFORMAT_MIME, XA_ANDROID_MIME_MP2TS, XA_CONTAINERTYPE_MPEG_TS};
XADataSource dataSrc = {&loc_abq, &format_mime};
// configure audio sink
XADataLocator_OutputMix loc_outmix = {XA_DATALOCATOR_OUTPUTMIX, video->outputMixObject};
XADataSink audioSnk = {&loc_outmix, NULL};
// configure image video sink
XADataLocator_NativeDisplay loc_nd = {
XA_DATALOCATOR_NATIVEDISPLAY, // locatorType
// the video sink must be an ANativeWindow created from a Surface or SurfaceTexture
(void *)video->theNativeWindow, // hWindow
// must be NULL
NULL // hDisplay
};
XADataSink imageVideoSink = {&loc_nd, NULL};
// declare interfaces to use
XAboolean required[NB_MAXAL_INTERFACES] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE};
XAInterfaceID iidArray[NB_MAXAL_INTERFACES] = {XA_IID_PLAY, XA_IID_ANDROIDBUFFERQUEUESOURCE, XA_IID_STREAMINFORMATION};
// create media player
res = (*video->engineEngine)
->CreateMediaPlayer(video->engineEngine, &video->playerObj, &dataSrc, NULL, &audioSnk, &imageVideoSink, NULL, NULL,
NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/, iidArray /*const XAInterfaceID *pInterfaceIds*/,
required /*const XAboolean *pInterfaceRequired*/);
assert(XA_RESULT_SUCCESS == res);
// realize the player
res = (*video->playerObj)->Realize(video->playerObj, XA_BOOLEAN_FALSE);
assert(XA_RESULT_SUCCESS == res);
// get the play interface
res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_PLAY, &video->playerPlayItf);
assert(XA_RESULT_SUCCESS == res);
// get the stream information interface (for video size)
res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_STREAMINFORMATION, &video->playerStreamInfoItf);
assert(XA_RESULT_SUCCESS == res);
// get the volume interface
res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_VOLUME, &video->playerVolItf);
assert(XA_RESULT_SUCCESS == res);
// get the Android buffer queue interface
res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_ANDROIDBUFFERQUEUESOURCE, &video->playerBQItf);
assert(XA_RESULT_SUCCESS == res);
// specify which events we want to be notified of
res = (*video->playerBQItf)->SetCallbackEventsMask(video->playerBQItf, XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED);
assert(XA_RESULT_SUCCESS == res);
// register the callback from which OpenMAX AL can retrieve the data to play
res = (*video->playerBQItf)->RegisterCallback(video->playerBQItf, AndroidBufferQueueCallback, video);
assert(XA_RESULT_SUCCESS == res);
// we want to be notified of the video size once it's found, so we register a callback for that
res = (*video->playerStreamInfoItf)->RegisterStreamChangeCallback(video->playerStreamInfoItf, StreamChangeCallback, video);
assert(XA_RESULT_SUCCESS == res);
// enqueue the initial buffers
if (!kinc_android_video_enqueue_initial_buffers(video, false)) {
kinc_log(KINC_LOG_LEVEL_INFO, "Could not enqueue initial buffers for video decoding.");
return false;
}
// prepare the player
res = (*video->playerPlayItf)->SetPlayState(video->playerPlayItf, XA_PLAYSTATE_PAUSED);
assert(XA_RESULT_SUCCESS == res);
// set the volume
res = (*video->playerVolItf)->SetVolumeLevel(video->playerVolItf, 0);
assert(XA_RESULT_SUCCESS == res);
// start the playback
res = (*video->playerPlayItf)->SetPlayState(video->playerPlayItf, XA_PLAYSTATE_PLAYING);
assert(XA_RESULT_SUCCESS == res);
kinc_log(KINC_LOG_LEVEL_INFO, "Successfully loaded video.");
return true;
}
void kinc_android_video_shutdown(kinc_android_video_t *video) {
// destroy streaming media player object, and invalidate all associated interfaces
if (video->playerObj != NULL) {
(*video->playerObj)->Destroy(video->playerObj);
video->playerObj = NULL;
video->playerPlayItf = NULL;
video->playerBQItf = NULL;
video->playerStreamInfoItf = NULL;
video->playerVolItf = NULL;
}
// destroy output mix object, and invalidate all associated interfaces
if (video->outputMixObject != NULL) {
(*video->outputMixObject)->Destroy(video->outputMixObject);
video->outputMixObject = NULL;
}
// destroy engine object, and invalidate all associated interfaces
if (video->engineObject != NULL) {
(*video->engineObject)->Destroy(video->engineObject);
video->engineObject = NULL;
video->engineEngine = NULL;
}
// close the file
if (video->file != NULL) {
AAsset_close(video->file);
video->file = NULL;
}
// make sure we don't leak native windows
if (video->theNativeWindow != NULL) {
ANativeWindow_release(video->theNativeWindow);
video->theNativeWindow = NULL;
}
}
#endif
JNIEXPORT void JNICALL Java_tech_kinc_KincMoviePlayer_nativeCreate(JNIEnv *env, jobject jobj, jstring jpath, jobject surface, jint id) {
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
const char *path = (*env)->GetStringUTFChars(env, jpath, NULL);
kinc_android_video_t *av = malloc(sizeof *av);
kinc_android_video_init(av);
av->theNativeWindow = ANativeWindow_fromSurface(env, surface);
kinc_android_video_open(av, path);
for (int i = 0; i < 10; ++i) {
if (videos[i] != NULL && videos[i]->impl.id == id) {
videos[i]->impl.androidVideo = av;
break;
}
}
(*env)->ReleaseStringUTFChars(env, jpath, path);
#endif
}
void KoreAndroidVideoInit() {
JNIEnv *env;
(*kinc_android_get_activity()->vm)->AttachCurrentThread(kinc_android_get_activity()->vm, &env, NULL);
jclass clazz = kinc_android_find_class(env, "tech.kinc.KincMoviePlayer");
// String path, Surface surface, int id
JNINativeMethod methodTable[] = {{"nativeCreate", "(Ljava/lang/String;Landroid/view/Surface;I)V", (void *)Java_tech_kinc_KincMoviePlayer_nativeCreate}};
int methodTableSize = sizeof(methodTable) / sizeof(methodTable[0]);
int failure = (*env)->RegisterNatives(env, clazz, methodTable, methodTableSize);
if (failure != 0) {
kinc_log(KINC_LOG_LEVEL_WARNING, "Failed to register KincMoviePlayer.nativeCreate");
}
(*kinc_android_get_activity()->vm)->DetachCurrentThread(kinc_android_get_activity()->vm);
}
void kinc_video_init(kinc_video_t *video, const char *filename) {
video->impl.playing = false;
video->impl.sound = NULL;
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
kinc_log(KINC_LOG_LEVEL_INFO, "Opening video %s.", filename);
video->impl.myWidth = 1023;
video->impl.myHeight = 684;
video->impl.next = 0;
video->impl.audioTime = 0;
JNIEnv *env = NULL;
(*kinc_android_get_activity()->vm)->AttachCurrentThread(kinc_android_get_activity()->vm, &env, NULL);
jclass koreMoviePlayerClass = kinc_android_find_class(env, "tech.kinc.KincMoviePlayer");
jmethodID constructor = (*env)->GetMethodID(env, koreMoviePlayerClass, "<init>", "(Ljava/lang/String;)V");
jobject object = (*env)->NewObject(env, koreMoviePlayerClass, constructor, (*env)->NewStringUTF(env, filename));
jmethodID getId = (*env)->GetMethodID(env, koreMoviePlayerClass, "getId", "()I");
video->impl.id = (*env)->CallIntMethod(env, object, getId);
for (int i = 0; i < videosCount; ++i) {
if (videos[i] == NULL) {
videos[i] = video;
break;
}
}
jmethodID jinit = (*env)->GetMethodID(env, koreMoviePlayerClass, "init", "()V");
(*env)->CallVoidMethod(env, object, jinit);
jmethodID getTextureId = (*env)->GetMethodID(env, koreMoviePlayerClass, "getTextureId", "()I");
int texid = (*env)->CallIntMethod(env, object, getTextureId);
(*kinc_android_get_activity()->vm)->DetachCurrentThread(kinc_android_get_activity()->vm);
kinc_g4_texture_init_from_id(&video->impl.image, texid);
#endif
}
void kinc_video_destroy(kinc_video_t *video) {
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
kinc_video_stop(video);
kinc_android_video_t *av = (kinc_android_video_t *)video->impl.androidVideo;
kinc_android_video_shutdown(av);
for (int i = 0; i < 10; ++i) {
if (videos[i] == video) {
videos[i] = NULL;
break;
}
}
#endif
}
void kinc_video_play(kinc_video_t *video, bool loop) {
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
video->impl.playing = true;
video->impl.start = kinc_time();
#endif
}
void kinc_video_pause(kinc_video_t *video) {
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
video->impl.playing = false;
#endif
}
void kinc_video_stop(kinc_video_t *video) {
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
kinc_video_pause(video);
#endif
}
void kinc_video_update(kinc_video_t *video, double time) {}
int kinc_video_width(kinc_video_t *video) {
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
return video->impl.myWidth;
#else
return 512;
#endif
}
int kinc_video_height(kinc_video_t *video) {
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
return video->impl.myHeight;
#else
return 512;
#endif
}
kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) {
#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN)
return &video->impl.image;
#else
return NULL;
#endif
}
double kinc_video_duration(kinc_video_t *video) {
return 0.0;
}
double kinc_video_position(kinc_video_t *video) {
return 0.0;
}
bool kinc_video_finished(kinc_video_t *video) {
return false;
}
bool kinc_video_paused(kinc_video_t *video) {
return !video->impl.playing;
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <kinc/graphics4/texture.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void *assetReader;
void *videoTrackOutput;
void *audioTrackOutput;
double start;
double next;
// double audioTime;
unsigned long long audioTime;
bool playing;
void *sound;
void *androidVideo;
int id;
kinc_g4_texture_t image;
double lastTime;
int myWidth;
int myHeight;
} kinc_video_impl_t;
typedef struct kinc_internal_video_sound_stream {
void *audioTrackOutput;
float *buffer;
int bufferSize;
int bufferWritePosition;
int bufferReadPosition;
uint64_t read;
uint64_t written;
} kinc_internal_video_sound_stream_t;
void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency);
void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream);
void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count);
float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream);
bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,79 @@
#include <kinc/display.h>
#include <kinc/graphics4/graphics.h>
#include <kinc/window.h>
static void (*resizeCallback)(int x, int y, void *data) = NULL;
static void *resizeCallbackData = NULL;
int kinc_count_windows(void) {
return 1;
}
int kinc_window_x(int window_index) {
return 0;
}
int kinc_window_y(int window_index) {
return 0;
}
int kinc_android_width();
int kinc_window_width(int window_index) {
return kinc_android_width();
}
int kinc_android_height();
int kinc_window_height(int window_index) {
return kinc_android_height();
}
void kinc_window_resize(int window_index, int width, int height) {}
void kinc_window_move(int window_index, int x, int y) {}
void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame);
void kinc_window_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) {
kinc_internal_change_framebuffer(0, frame);
}
void kinc_window_change_features(int window_index, int features) {}
void kinc_window_change_mode(int window_index, kinc_window_mode_t mode) {}
void kinc_window_destroy(int window_index) {}
void kinc_window_show(int window_index) {}
void kinc_window_hide(int window_index) {}
void kinc_window_set_title(int window_index, const char *title) {}
int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) {
return 0;
}
void kinc_window_set_resize_callback(int window_index, void (*callback)(int x, int y, void *data), void *data) {
resizeCallback = callback;
resizeCallbackData = data;
}
void kinc_internal_call_resize_callback(int window_index, int width, int height) {
if (resizeCallback != NULL) {
resizeCallback(width, height, resizeCallbackData);
}
}
void kinc_window_set_ppi_changed_callback(int window_index, void (*callback)(int ppi, void *data), void *data) {}
void kinc_window_set_close_callback(int window, bool (*callback)(void *), void *data) {}
kinc_window_mode_t kinc_window_get_mode(int window_index) {
return KINC_WINDOW_MODE_FULLSCREEN;
}
int kinc_window_display(int window) {
return 0;
}

View File

@ -0,0 +1 @@
#pragma once

View File

@ -0,0 +1,4 @@
#include "http.m.h"
#include "system.m.h"
#include "thread.m.h"
#include "video.m.h"

View File

@ -0,0 +1,53 @@
#include <kinc/network/http.h>
#import <Foundation/Foundation.h>
void kinc_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header,
kinc_http_callback_t callback, void *callbackdata) {
NSString *urlstring = secure ? @"https://" : @"http://";
urlstring = [urlstring stringByAppendingString:[NSString stringWithUTF8String:url]];
urlstring = [urlstring stringByAppendingString:@":"];
urlstring = [urlstring stringByAppendingString:[[NSNumber numberWithInt:port] stringValue]];
urlstring = [urlstring stringByAppendingString:@"/"];
urlstring = [urlstring stringByAppendingString:[NSString stringWithUTF8String:path]];
NSURL *aUrl = [NSURL URLWithString:urlstring];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPAdditionalHeaders = @{@"Content-Type" : @"application/json"};
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl];
if (data != 0) {
// printf("Sending %s\n\n", data);
NSString *datastring = [NSString stringWithUTF8String:data];
request.HTTPBody = [datastring dataUsingEncoding:NSUTF8StringEncoding];
}
switch (method) {
case KINC_HTTP_GET:
request.HTTPMethod = @"GET";
break;
case KINC_HTTP_POST:
request.HTTPMethod = @"POST";
break;
case KINC_HTTP_PUT:
request.HTTPMethod = @"PUT";
break;
case KINC_HTTP_DELETE:
request.HTTPMethod = @"DELETE";
break;
}
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
int statusCode = (int)[httpResponse statusCode];
NSMutableData *responseData = [[NSMutableData alloc] init];
[responseData appendData:data];
[responseData appendBytes:"\0" length:1];
callback(error == nil ? 0 : 1, statusCode, (const char *)[responseData bytes], callbackdata);
}];
[dataTask resume];
}

View File

@ -0,0 +1,24 @@
#include <kinc/system.h>
int kinc_hardware_threads(void) {
return (int)[[NSProcessInfo processInfo] processorCount];
}
#ifdef KINC_APPLE_SOC
int kinc_cpu_cores(void) {
return kinc_hardware_threads();
}
#else
#include <sys/sysctl.h>
int kinc_cpu_cores(void) {
uint32_t proper_cpu_count = 1;
size_t count_length = sizeof(count_length);
sysctlbyname("hw.physicalcpu", &proper_cpu_count, &count_length, 0, 0);
return (int)proper_cpu_count;
}
#endif

View File

@ -0,0 +1,50 @@
#include <stdio.h>
#include <string.h>
#include <Foundation/Foundation.h>
#include <kinc/threads/mutex.h>
#include <kinc/threads/thread.h>
#include <pthread.h>
#include <stdio.h>
#include <wchar.h>
static void *ThreadProc(void *arg) {
@autoreleasepool {
kinc_thread_t *t = (kinc_thread_t *)arg;
t->impl.thread(t->impl.param);
pthread_exit(NULL);
return NULL;
}
}
void kinc_thread_init(kinc_thread_t *t, void (*thread)(void *param), void *param) {
t->impl.param = param;
t->impl.thread = thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
// pthread_attr_setstacksize(&attr, 1024 * 64);
struct sched_param sp;
memset(&sp, 0, sizeof(sp));
sp.sched_priority = 0;
pthread_attr_setschedparam(&attr, &sp);
pthread_create(&t->impl.pthread, &attr, &ThreadProc, t);
// Kt::affirmD(ret == 0);
pthread_attr_destroy(&attr);
}
void kinc_thread_wait_and_destroy(kinc_thread_t *thread) {
int ret;
do {
ret = pthread_join(thread->impl.pthread, NULL);
} while (ret != 0);
}
bool kinc_thread_try_to_destroy(kinc_thread_t *thread) {
return pthread_join(thread->impl.pthread, NULL) == 0;
}
void kinc_threads_init(void) {}
void kinc_threads_quit(void) {}

View File

@ -0,0 +1,56 @@
#pragma once
#include <objc/runtime.h>
#include <kinc/graphics4/texture.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
double start;
double videoStart;
double next;
// double audioTime;
unsigned long long audioTime;
bool playing;
bool loop;
void *sound;
bool image_initialized;
kinc_g4_texture_t image;
double lastTime;
float duration;
bool finished;
int myWidth;
int myHeight;
id videoAsset;
id assetReader;
id videoTrackOutput;
id audioTrackOutput;
id url;
} kinc_video_impl_t;
typedef struct kinc_internal_video_sound_stream {
float *buffer;
int bufferSize;
int bufferWritePosition;
int bufferReadPosition;
uint64_t read;
uint64_t written;
} kinc_internal_video_sound_stream_t;
void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency);
void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream);
void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count);
float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream);
bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,311 @@
#include <kinc/video.h>
#import <AVFoundation/AVFoundation.h>
#include <kinc/audio1/audio.h>
#include <kinc/graphics4/texture.h>
#include <kinc/io/filereader.h>
#include <kinc/log.h>
#include <kinc/system.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern const char *iphonegetresourcepath(void);
extern const char *macgetresourcepath(void);
void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {
stream->bufferSize = 1024 * 100;
stream->bufferReadPosition = 0;
stream->bufferWritePosition = 0;
stream->read = 0;
stream->written = 0;
stream->buffer = (float *)malloc(stream->bufferSize * sizeof(float));
}
void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) {
free(stream->buffer);
}
void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {
for (int i = 0; i < sample_count; ++i) {
float value = data[i]; // / 32767.0;
stream->buffer[stream->bufferWritePosition++] = value;
++stream->written;
if (stream->bufferWritePosition >= stream->bufferSize) {
stream->bufferWritePosition = 0;
}
}
}
static float samples[2] = {0};
float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) {
++stream->read;
if (stream->written <= stream->read) {
kinc_log(KINC_LOG_LEVEL_WARNING, "Out of audio\n");
return 0;
}
if (stream->bufferReadPosition >= stream->bufferSize) {
stream->bufferReadPosition = 0;
kinc_log(KINC_LOG_LEVEL_INFO, "buffer read back - %i\n", (int)(stream->written - stream->read));
}
samples[0] = stream->buffer[stream->bufferReadPosition++];
if (stream->bufferReadPosition >= stream->bufferSize) {
stream->bufferReadPosition = 0;
kinc_log(KINC_LOG_LEVEL_INFO, "buffer read back - %i\n", (int)(stream->written - stream->read));
}
samples[1] = stream->buffer[stream->bufferReadPosition++];
return samples;
}
bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) {
return false;
}
static void load(kinc_video_t *video, double startTime) {
video->impl.videoStart = startTime;
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:video->impl.url options:nil];
video->impl.videoAsset = asset;
video->impl.duration = [asset duration].value / [asset duration].timescale;
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
NSDictionary *videoOutputSettings =
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, nil];
AVAssetReaderTrackOutput *videoOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:videoOutputSettings];
[videoOutput setSupportsRandomAccess:YES];
bool hasAudio = [[asset tracksWithMediaType:AVMediaTypeAudio] count] > 0;
AVAssetReaderAudioMixOutput *audioOutput = NULL;
if (hasAudio) {
AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
NSDictionary *audioOutputSettings = [NSDictionary
dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, [NSNumber numberWithFloat:44100.0], AVSampleRateKey,
[NSNumber numberWithInt:32], AVLinearPCMBitDepthKey, [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
[NSNumber numberWithBool:YES], AVLinearPCMIsFloatKey, [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey, nil];
audioOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:@[ audioTrack ] audioSettings:audioOutputSettings];
[audioOutput setSupportsRandomAccess:YES];
}
AVAssetReader *reader = [AVAssetReader assetReaderWithAsset:asset error:nil];
if (startTime > 0) {
CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(startTime * 1000, 1000), kCMTimePositiveInfinity);
reader.timeRange = timeRange;
}
[reader addOutput:videoOutput];
if (hasAudio) {
[reader addOutput:audioOutput];
}
video->impl.assetReader = reader;
video->impl.videoTrackOutput = videoOutput;
if (hasAudio) {
video->impl.audioTrackOutput = audioOutput;
}
else {
video->impl.audioTrackOutput = NULL;
}
if (video->impl.myWidth < 0)
video->impl.myWidth = [videoTrack naturalSize].width;
if (video->impl.myHeight < 0)
video->impl.myHeight = [videoTrack naturalSize].height;
int framerate = [videoTrack nominalFrameRate];
kinc_log(KINC_LOG_LEVEL_INFO, "Framerate: %i\n", framerate);
video->impl.next = video->impl.videoStart;
video->impl.audioTime = video->impl.videoStart * 44100;
}
void kinc_video_init(kinc_video_t *video, const char *filename) {
video->impl.playing = false;
video->impl.sound = NULL;
video->impl.image_initialized = false;
char name[2048];
#ifdef KINC_IOS
strcpy(name, iphonegetresourcepath());
#else
strcpy(name, macgetresourcepath());
#endif
strcat(name, "/");
strcat(name, KINC_DEBUGDIR);
strcat(name, "/");
strcat(name, filename);
video->impl.url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:name]];
video->impl.myWidth = -1;
video->impl.myHeight = -1;
video->impl.finished = false;
video->impl.duration = 0;
load(video, 0);
}
void kinc_video_destroy(kinc_video_t *video) {
kinc_video_stop(video);
}
#ifdef KINC_IOS
void iosPlayVideoSoundStream(kinc_internal_video_sound_stream_t *video);
void iosStopVideoSoundStream(void);
#else
void macPlayVideoSoundStream(kinc_internal_video_sound_stream_t *video);
void macStopVideoSoundStream(void);
#endif
void kinc_video_play(kinc_video_t *video, bool loop) {
AVAssetReader *reader = video->impl.assetReader;
[reader startReading];
kinc_internal_video_sound_stream_t *stream = (kinc_internal_video_sound_stream_t *)malloc(sizeof(kinc_internal_video_sound_stream_t));
kinc_internal_video_sound_stream_init(stream, 2, 44100);
video->impl.sound = stream;
#ifdef KINC_IOS
iosPlayVideoSoundStream((kinc_internal_video_sound_stream_t *)video->impl.sound);
#else
macPlayVideoSoundStream((kinc_internal_video_sound_stream_t *)video->impl.sound);
#endif
video->impl.playing = true;
video->impl.start = kinc_time() - video->impl.videoStart;
video->impl.loop = loop;
}
void kinc_video_pause(kinc_video_t *video) {
video->impl.playing = false;
if (video->impl.sound != NULL) {
// Mixer::stop(sound);
#ifdef KINC_IOS
iosStopVideoSoundStream();
#else
macStopVideoSoundStream();
#endif
kinc_internal_video_sound_stream_destroy((kinc_internal_video_sound_stream_t *)video->impl.sound);
free(video->impl.sound);
video->impl.sound = NULL;
}
}
void kinc_video_stop(kinc_video_t *video) {
kinc_video_pause(video);
video->impl.finished = true;
}
static void updateImage(kinc_video_t *video) {
if (!video->impl.playing)
return;
{
AVAssetReaderTrackOutput *videoOutput = video->impl.videoTrackOutput;
CMSampleBufferRef buffer = [videoOutput copyNextSampleBuffer];
if (!buffer) {
if (video->impl.loop) {
CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(0, 1000), kCMTimePositiveInfinity);
[videoOutput resetForReadingTimeRanges:[NSArray arrayWithObject:[NSValue valueWithCMTimeRange:timeRange]]];
AVAssetReaderAudioMixOutput *audioOutput = video->impl.audioTrackOutput;
CMSampleBufferRef audio_buffer = [audioOutput copyNextSampleBuffer];
while (audio_buffer) {
audio_buffer = [audioOutput copyNextSampleBuffer];
}
[audioOutput resetForReadingTimeRanges:[NSArray arrayWithObject:[NSValue valueWithCMTimeRange:timeRange]]];
buffer = [videoOutput copyNextSampleBuffer];
video->impl.start = kinc_time() - video->impl.videoStart;
}
else {
kinc_video_stop(video);
return;
}
}
video->impl.next = CMTimeGetSeconds(CMSampleBufferGetOutputPresentationTimeStamp(buffer));
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(buffer);
if (!video->impl.image_initialized) {
CGSize size = CVImageBufferGetDisplaySize(pixelBuffer);
video->impl.myWidth = size.width;
video->impl.myHeight = size.height;
kinc_g4_texture_init(&video->impl.image, kinc_video_width(video), kinc_video_height(video), KINC_IMAGE_FORMAT_BGRA32);
video->impl.image_initialized = true;
}
if (pixelBuffer != NULL) {
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
#ifdef KINC_OPENGL
kinc_g4_texture_upload(&video->impl.image, (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer),
(int)(CVPixelBufferGetBytesPerRow(pixelBuffer) / 4));
#else
kinc_g4_texture_upload(&video->impl.image, (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer), (int)(CVPixelBufferGetBytesPerRow(pixelBuffer)));
#endif
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
}
CFRelease(buffer);
}
if (video->impl.audioTrackOutput != NULL) {
AVAssetReaderAudioMixOutput *audioOutput = video->impl.audioTrackOutput;
while (video->impl.audioTime / 44100.0 < video->impl.next + 0.1) {
CMSampleBufferRef buffer = [audioOutput copyNextSampleBuffer];
if (!buffer)
return;
CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(buffer);
AudioBufferList audioBufferList;
CMBlockBufferRef blockBufferOut = nil;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(buffer, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBufferOut);
for (int bufferCount = 0; bufferCount < audioBufferList.mNumberBuffers; ++bufferCount) {
float *samples = (float *)audioBufferList.mBuffers[bufferCount].mData;
kinc_internal_video_sound_stream_t *sound = (kinc_internal_video_sound_stream_t *)video->impl.sound;
if (video->impl.audioTime / 44100.0 > video->impl.next - 0.1) {
kinc_internal_video_sound_stream_insert_data(sound, samples, (int)numSamplesInBuffer * 2);
}
else {
// Send some data anyway because the buffers are huge
kinc_internal_video_sound_stream_insert_data(sound, samples, (int)numSamplesInBuffer);
}
video->impl.audioTime += numSamplesInBuffer;
}
CFRelease(blockBufferOut);
CFRelease(buffer);
}
}
}
void kinc_video_update(kinc_video_t *video, double time) {
if (video->impl.playing && time >= video->impl.start + video->impl.next) {
updateImage(video);
}
}
int kinc_video_width(kinc_video_t *video) {
return video->impl.myWidth;
}
int kinc_video_height(kinc_video_t *video) {
return video->impl.myHeight;
}
kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) {
kinc_video_update(video, kinc_time());
return &video->impl.image;
}
double kinc_video_duration(kinc_video_t *video) {
return video->impl.duration;
}
bool kinc_video_finished(kinc_video_t *video) {
return video->impl.finished;
}
bool kinc_video_paused(kinc_video_t *video) {
return !video->impl.playing;
}
double kinc_video_position(kinc_video_t *video) {
return video->impl.next - video->impl.start;
}

View File

@ -0,0 +1,13 @@
#pragma once
#define KINC_ATOMIC_COMPARE_EXCHANGE(pointer, oldValue, newValue)
#define KINC_ATOMIC_COMPARE_EXCHANGE_POINTER(pointer, oldValue, newValue)
#define KINC_ATOMIC_INCREMENT(pointer)
#define KINC_ATOMIC_DECREMENT(pointer)
#define KINC_ATOMIC_EXCHANGE_32(pointer, value)
#define KINC_ATOMIC_EXCHANGE_FLOAT(pointer, value)

View File

@ -0,0 +1,118 @@
#include <AL/al.h>
#include <AL/alc.h>
#include <assert.h>
#include <emscripten.h>
#include <kinc/audio2/audio.h>
#include <stdio.h>
#include <stdlib.h>
static kinc_a2_buffer_t a2_buffer;
static ALCdevice *device = NULL;
static ALCcontext *context = NULL;
static unsigned int channels = 0;
static unsigned int bits = 0;
static ALenum format = 0;
static ALuint source = 0;
static bool audioRunning = false;
#define BUFSIZE 4096
static short buf[BUFSIZE];
#define NUM_BUFFERS 3
static uint32_t samples_per_second = 44100;
static void copySample(void *buffer) {
float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location];
float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location];
a2_buffer.read_location += 1;
if (a2_buffer.read_location >= a2_buffer.data_size) {
a2_buffer.read_location = 0;
}
((int16_t *)buffer)[0] = (int16_t)(left_value * 32767);
((int16_t *)buffer)[1] = (int16_t)(right_value * 32767);
}
static void streamBuffer(ALuint buffer) {
if (kinc_a2_internal_callback(&a2_buffer, BUFSIZE / 2)) {
for (int i = 0; i < BUFSIZE; i += 2) {
copySample(&buf[i]);
}
}
alBufferData(buffer, format, buf, BUFSIZE * 2, samples_per_second);
}
static void iter() {
if (!audioRunning) {
return;
}
ALint processed;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
if (processed <= 0) {
return;
}
while (processed--) {
ALuint buffer;
alSourceUnqueueBuffers(source, 1, &buffer);
streamBuffer(buffer);
alSourceQueueBuffers(source, 1, &buffer);
}
ALint playing;
alGetSourcei(source, AL_SOURCE_STATE, &playing);
if (playing != AL_PLAYING) {
alSourcePlay(source);
}
}
static bool a2_initialized = false;
void kinc_a2_init() {
if (a2_initialized) {
return;
}
kinc_a2_internal_init();
a2_initialized = true;
a2_buffer.read_location = 0;
a2_buffer.write_location = 0;
a2_buffer.data_size = 128 * 1024;
a2_buffer.channel_count = 2;
a2_buffer.channels[0] = (float*)malloc(a2_buffer.data_size * sizeof(float));
a2_buffer.channels[1] = (float*)malloc(a2_buffer.data_size * sizeof(float));
audioRunning = true;
device = alcOpenDevice(NULL);
context = alcCreateContext(device, NULL);
alcMakeContextCurrent(context);
format = AL_FORMAT_STEREO16;
ALuint buffers[NUM_BUFFERS];
alGenBuffers(NUM_BUFFERS, buffers);
alGenSources(1, &source);
streamBuffer(buffers[0]);
streamBuffer(buffers[1]);
streamBuffer(buffers[2]);
alSourceQueueBuffers(source, NUM_BUFFERS, buffers);
alSourcePlay(source);
}
void kinc_a2_update() {
iter();
}
void kinc_a2_shutdown() {
audioRunning = false;
}
uint32_t kinc_a2_samples_per_second(void) {
return samples_per_second;
}

View File

@ -0,0 +1,47 @@
#include <kinc/display.h>
void kinc_display_init(void) {}
int kinc_primary_display(void) {
return 0;
}
int kinc_count_displays(void) {
return 1;
}
bool kinc_display_available(int display_index) {
return false;
}
const char *kinc_display_name(int display_index) {
return "Browser";
}
kinc_display_mode_t kinc_display_current_mode(int display_index) {
kinc_display_mode_t mode;
mode.x = 0;
mode.y = 0;
mode.width = 800;
mode.height = 600;
mode.pixels_per_inch = 96;
mode.frequency = 60;
mode.bits_per_pixel = 32;
return mode;
}
int kinc_display_count_available_modes(int display_index) {
return 1;
}
kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index) {
kinc_display_mode_t mode;
mode.x = 0;
mode.y = 0;
mode.width = 800;
mode.height = 600;
mode.pixels_per_inch = 96;
mode.frequency = 60;
mode.bits_per_pixel = 32;
return mode;
}

View File

@ -0,0 +1,15 @@
#include <kinc/threads/event.h>
void kinc_event_init(kinc_event_t *event, bool auto_reset) {}
void kinc_event_destroy(kinc_event_t *event) {}
void kinc_event_signal(kinc_event_t *event) {}
void kinc_event_wait(kinc_event_t *event) {}
bool kinc_event_try_to_wait(kinc_event_t *event, double seconds) {
return false;
}
void kinc_event_reset(kinc_event_t *event) {}

View File

@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int nothing;
} kinc_event_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,15 @@
#include <kinc/input/gamepad.h>
const char *kinc_gamepad_vendor(int gamepad) {
return "None";
}
const char *kinc_gamepad_product_name(int gamepad) {
return "Gamepad";
}
bool kinc_gamepad_connected(int gamepad) {
return false;
}
void kinc_gamepad_rumble(int gamepad, float left, float right) {}

View File

@ -0,0 +1,12 @@
#include "audio.c.h"
#include "display.c.h"
#include "event.c.h"
#include "gamepad.c.h"
#include "mouse.c.h"
#include "mutex.c.h"
#include "semaphore.c.h"
#include "system.c.h"
#include "thread.c.h"
#include "threadlocal.c.h"
#include "video.c.h"
#include "window.c.h"

View File

@ -0,0 +1,17 @@
#include <kinc/input/mouse.h>
void kinc_internal_mouse_lock(int window) {}
void kinc_internal_mouse_unlock(void) {}
bool kinc_mouse_can_lock(void) {
return false;
}
void kinc_mouse_show() {}
void kinc_mouse_hide() {}
void kinc_mouse_set_position(int window, int x, int y) {}
void kinc_mouse_get_position(int window, int *x, int *y) {}

View File

@ -0,0 +1,23 @@
#include <kinc/threads/mutex.h>
void kinc_mutex_init(kinc_mutex_t *mutex) {}
void kinc_mutex_destroy(kinc_mutex_t *mutex) {}
bool kinc_mutex_try_to_lock(kinc_mutex_t *mutex) {
return false;
}
void kinc_mutex_lock(kinc_mutex_t *mutex) {}
void kinc_mutex_unlock(kinc_mutex_t *mutex) {}
bool kinc_uber_mutex_init(kinc_uber_mutex_t *mutex, const char *name) {
return false;
}
void kinc_uber_mutex_destroy(kinc_uber_mutex_t *mutex) {}
void kinc_uber_mutex_lock(kinc_uber_mutex_t *mutex) {}
void kinc_uber_mutex_unlock(kinc_uber_mutex_t *mutex) {}

View File

@ -0,0 +1,17 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int nothing;
} kinc_mutex_impl_t;
typedef struct {
int nothing;
} kinc_uber_mutex_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,13 @@
#include <kinc/threads/semaphore.h>
void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max) {}
void kinc_semaphore_destroy(kinc_semaphore_t *semaphore) {}
void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count) {}
void kinc_semaphore_acquire(kinc_semaphore_t *semaphore) {}
bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double seconds) {
return false;
}

View File

@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int nothing;
} kinc_semaphore_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,350 @@
#ifdef KINC_OPENGL
#include <GL/glfw.h>
#endif
#include <emscripten/emscripten.h>
#include <kinc/audio2/audio.h>
#include <kinc/graphics4/graphics.h>
#include <kinc/input/keyboard.h>
#include <kinc/input/mouse.h>
#include <kinc/log.h>
#include <kinc/system.h>
#include <kinc/window.h>
#include <stdio.h>
#include <stdlib.h>
static int html5_argc;
static char **html5_argv;
static bool initialized = false;
static void drawfunc() {
if (!initialized)
return;
kinc_internal_update_callback();
kinc_a2_update();
#ifdef KINC_OPENGL
glfwSwapBuffers();
#endif
}
#define KEY_DOWN(GLFW_KEYCODE, KINC_KEY) \
case GLFW_KEYCODE: \
kinc_internal_keyboard_trigger_key_down(KINC_KEY); \
break;
#define KEY_UP(GLFW_KEYCODE, KINC_KEY) \
case GLFW_KEYCODE: \
kinc_internal_keyboard_trigger_key_up(KINC_KEY); \
break;
#ifdef KINC_OPENGL
// glfw mappings as state here: https://www.glfw.org/docs/3.3/group__keys.html
static void onKeyPressed(int key, int action) {
if (action == GLFW_PRESS) {
switch (key) {
KEY_DOWN(32, KINC_KEY_SPACE)
KEY_DOWN(39, KINC_KEY_QUOTE)
KEY_DOWN(44, KINC_KEY_COMMA)
KEY_DOWN(45, KINC_KEY_SUBTRACT)
KEY_DOWN(46, KINC_KEY_PERIOD)
KEY_DOWN(47, KINC_KEY_SLASH)
KEY_DOWN(48, KINC_KEY_0)
KEY_DOWN(49, KINC_KEY_1)
KEY_DOWN(50, KINC_KEY_2)
KEY_DOWN(51, KINC_KEY_3)
KEY_DOWN(52, KINC_KEY_4)
KEY_DOWN(53, KINC_KEY_5)
KEY_DOWN(54, KINC_KEY_6)
KEY_DOWN(55, KINC_KEY_7)
KEY_DOWN(56, KINC_KEY_8)
KEY_DOWN(57, KINC_KEY_9)
KEY_DOWN(59, KINC_KEY_SEMICOLON)
KEY_DOWN(61, KINC_KEY_EQUALS)
KEY_DOWN(65, KINC_KEY_A)
KEY_DOWN(66, KINC_KEY_B)
KEY_DOWN(67, KINC_KEY_C)
KEY_DOWN(68, KINC_KEY_D)
KEY_DOWN(69, KINC_KEY_E)
KEY_DOWN(70, KINC_KEY_F)
KEY_DOWN(71, KINC_KEY_G)
KEY_DOWN(72, KINC_KEY_H)
KEY_DOWN(73, KINC_KEY_I)
KEY_DOWN(74, KINC_KEY_J)
KEY_DOWN(75, KINC_KEY_K)
KEY_DOWN(76, KINC_KEY_L)
KEY_DOWN(77, KINC_KEY_M)
KEY_DOWN(78, KINC_KEY_N)
KEY_DOWN(79, KINC_KEY_O)
KEY_DOWN(80, KINC_KEY_P)
KEY_DOWN(81, KINC_KEY_Q)
KEY_DOWN(82, KINC_KEY_R)
KEY_DOWN(83, KINC_KEY_S)
KEY_DOWN(84, KINC_KEY_T)
KEY_DOWN(85, KINC_KEY_U)
KEY_DOWN(86, KINC_KEY_V)
KEY_DOWN(87, KINC_KEY_W)
KEY_DOWN(88, KINC_KEY_X)
KEY_DOWN(89, KINC_KEY_Y)
KEY_DOWN(90, KINC_KEY_Z)
KEY_DOWN(92, KINC_KEY_BACK_SLASH)
KEY_DOWN(256, KINC_KEY_ESCAPE)
KEY_DOWN(257, KINC_KEY_RETURN)
KEY_DOWN(258, KINC_KEY_TAB)
KEY_DOWN(259, KINC_KEY_BACKSPACE)
KEY_DOWN(260, KINC_KEY_INSERT)
KEY_DOWN(261, KINC_KEY_DELETE)
KEY_DOWN(262, KINC_KEY_RIGHT)
KEY_DOWN(263, KINC_KEY_LEFT)
KEY_DOWN(264, KINC_KEY_DOWN)
KEY_DOWN(265, KINC_KEY_UP)
KEY_DOWN(268, KINC_KEY_HOME)
KEY_DOWN(269, KINC_KEY_END)
KEY_DOWN(284, KINC_KEY_PAUSE)
KEY_DOWN(290, KINC_KEY_F1)
KEY_DOWN(291, KINC_KEY_F2)
KEY_DOWN(292, KINC_KEY_F3)
KEY_DOWN(293, KINC_KEY_F4)
KEY_DOWN(294, KINC_KEY_F5)
KEY_DOWN(295, KINC_KEY_F6)
KEY_DOWN(296, KINC_KEY_F7)
KEY_DOWN(297, KINC_KEY_F8)
KEY_DOWN(298, KINC_KEY_F9)
KEY_DOWN(299, KINC_KEY_F10)
KEY_DOWN(300, KINC_KEY_F11)
KEY_DOWN(301, KINC_KEY_F12)
KEY_DOWN(302, KINC_KEY_F13)
KEY_DOWN(303, KINC_KEY_F14)
KEY_DOWN(304, KINC_KEY_F15)
KEY_DOWN(305, KINC_KEY_F16)
KEY_DOWN(306, KINC_KEY_F17)
KEY_DOWN(307, KINC_KEY_F18)
KEY_DOWN(308, KINC_KEY_F19)
KEY_DOWN(309, KINC_KEY_F20)
KEY_DOWN(310, KINC_KEY_F21)
KEY_DOWN(311, KINC_KEY_F22)
KEY_DOWN(312, KINC_KEY_F23)
KEY_DOWN(313, KINC_KEY_F24)
KEY_DOWN(348, KINC_KEY_CONTEXT_MENU)
}
}
else {
switch (key) {
KEY_UP(32, KINC_KEY_SPACE)
KEY_UP(39, KINC_KEY_QUOTE)
KEY_UP(44, KINC_KEY_COMMA)
KEY_UP(45, KINC_KEY_SUBTRACT)
KEY_UP(46, KINC_KEY_PERIOD)
KEY_UP(47, KINC_KEY_SLASH)
KEY_UP(48, KINC_KEY_0)
KEY_UP(49, KINC_KEY_1)
KEY_UP(50, KINC_KEY_2)
KEY_UP(51, KINC_KEY_3)
KEY_UP(52, KINC_KEY_4)
KEY_UP(53, KINC_KEY_5)
KEY_UP(54, KINC_KEY_6)
KEY_UP(55, KINC_KEY_7)
KEY_UP(56, KINC_KEY_8)
KEY_UP(57, KINC_KEY_9)
KEY_UP(59, KINC_KEY_SEMICOLON)
KEY_UP(61, KINC_KEY_EQUALS)
KEY_UP(65, KINC_KEY_A)
KEY_UP(66, KINC_KEY_B)
KEY_UP(67, KINC_KEY_C)
KEY_UP(68, KINC_KEY_D)
KEY_UP(69, KINC_KEY_E)
KEY_UP(70, KINC_KEY_F)
KEY_UP(71, KINC_KEY_G)
KEY_UP(72, KINC_KEY_H)
KEY_UP(73, KINC_KEY_I)
KEY_UP(74, KINC_KEY_J)
KEY_UP(75, KINC_KEY_K)
KEY_UP(76, KINC_KEY_L)
KEY_UP(77, KINC_KEY_M)
KEY_UP(78, KINC_KEY_N)
KEY_UP(79, KINC_KEY_O)
KEY_UP(80, KINC_KEY_P)
KEY_UP(81, KINC_KEY_Q)
KEY_UP(82, KINC_KEY_R)
KEY_UP(83, KINC_KEY_S)
KEY_UP(84, KINC_KEY_T)
KEY_UP(85, KINC_KEY_U)
KEY_UP(86, KINC_KEY_V)
KEY_UP(87, KINC_KEY_W)
KEY_UP(88, KINC_KEY_X)
KEY_UP(89, KINC_KEY_Y)
KEY_UP(90, KINC_KEY_Z)
KEY_UP(92, KINC_KEY_BACK_SLASH)
KEY_UP(256, KINC_KEY_ESCAPE)
KEY_UP(257, KINC_KEY_RETURN)
KEY_UP(258, KINC_KEY_TAB)
KEY_UP(259, KINC_KEY_BACKSPACE)
KEY_UP(260, KINC_KEY_INSERT)
KEY_UP(261, KINC_KEY_DELETE)
KEY_UP(262, KINC_KEY_RIGHT)
KEY_UP(263, KINC_KEY_LEFT)
KEY_UP(264, KINC_KEY_DOWN)
KEY_UP(265, KINC_KEY_UP)
KEY_UP(268, KINC_KEY_HOME)
KEY_UP(269, KINC_KEY_END)
KEY_UP(284, KINC_KEY_PAUSE)
KEY_UP(290, KINC_KEY_F1)
KEY_UP(291, KINC_KEY_F2)
KEY_UP(292, KINC_KEY_F3)
KEY_UP(293, KINC_KEY_F4)
KEY_UP(294, KINC_KEY_F5)
KEY_UP(295, KINC_KEY_F6)
KEY_UP(296, KINC_KEY_F7)
KEY_UP(297, KINC_KEY_F8)
KEY_UP(298, KINC_KEY_F9)
KEY_UP(299, KINC_KEY_F10)
KEY_UP(300, KINC_KEY_F11)
KEY_UP(301, KINC_KEY_F12)
KEY_UP(302, KINC_KEY_F13)
KEY_UP(303, KINC_KEY_F14)
KEY_UP(304, KINC_KEY_F15)
KEY_UP(305, KINC_KEY_F16)
KEY_UP(306, KINC_KEY_F17)
KEY_UP(307, KINC_KEY_F18)
KEY_UP(308, KINC_KEY_F19)
KEY_UP(309, KINC_KEY_F20)
KEY_UP(310, KINC_KEY_F21)
KEY_UP(311, KINC_KEY_F22)
KEY_UP(312, KINC_KEY_F23)
KEY_UP(313, KINC_KEY_F24)
KEY_UP(348, KINC_KEY_CONTEXT_MENU)
}
}
}
static int mouseX = 0;
static int mouseY = 0;
static void onMouseClick(int button, int action) {
if (action == GLFW_PRESS) {
if (button == 0) {
kinc_internal_mouse_trigger_press(0, 0, mouseX, mouseY);
}
else if (button == 1) {
kinc_internal_mouse_trigger_press(0, 1, mouseX, mouseY);
}
}
else {
if (button == 0) {
kinc_internal_mouse_trigger_release(0, 0, mouseX, mouseY);
}
else if (button == 1) {
kinc_internal_mouse_trigger_release(0, 1, mouseX, mouseY);
}
}
}
static void onMouseMove(int x, int y) {
mouseX = x;
mouseY = y;
kinc_internal_mouse_trigger_move(0, x, y);
}
#endif
static int with, height;
extern int kinc_internal_window_width;
extern int kinc_internal_window_height;
#ifdef KINC_KONG
void kong_init(void);
#endif
int kinc_init(const char *name, int width, int height, kinc_window_options_t *win, kinc_framebuffer_options_t *frame) {
kinc_window_options_t defaultWin;
if (win == NULL) {
kinc_window_options_set_defaults(&defaultWin);
win = &defaultWin;
}
kinc_framebuffer_options_t defaultFrame;
if (frame == NULL) {
kinc_framebuffer_options_set_defaults(&defaultFrame);
frame = &defaultFrame;
}
win->width = width;
win->height = height;
#ifdef KINC_OPENGL
glfwInit();
glfwOpenWindow(width, height, 8, 8, 8, 0, frame->depth_bits, frame->stencil_bits, GLFW_WINDOW);
glfwSetWindowTitle(name);
glfwSetKeyCallback(onKeyPressed);
glfwSetMousePosCallback(onMouseMove);
glfwSetMouseButtonCallback(onMouseClick);
#endif
kinc_internal_window_width = width;
kinc_internal_window_height = height;
kinc_g4_internal_init();
kinc_g4_internal_init_window(0, frame->depth_bits, frame->stencil_bits, true);
#ifdef KINC_KONG
kong_init();
#endif
return 0;
}
bool kinc_internal_handle_messages() {
return true;
}
void kinc_set_keep_screen_on(bool on) {}
double kinc_frequency(void) {
return 1000.0;
}
kinc_ticks_t kinc_timestamp(void) {
#ifdef KINC_OPENGL
return (kinc_ticks_t)(glfwGetTime() * 1000.0);
#else
return (kinc_ticks_t)(0.0);
#endif
}
double kinc_time(void) {
#ifdef KINC_OPENGL
return glfwGetTime();
#else
return 0.0;
#endif
}
int kinc_cpu_cores(void) {
return 4;
}
int kinc_hardware_threads(void) {
return 4;
}
extern int kickstart(int argc, char **argv);
#ifdef KINC_WEBGPU
EMSCRIPTEN_KEEPALIVE void kinc_internal_webgpu_initialized() {
kickstart(html5_argc, html5_argv);
initialized = true;
}
#endif
int main(int argc, char **argv) {
html5_argc = argc;
html5_argv = argv;
#ifdef KINC_WEBGPU
char *code = "(async () => {\
const adapter = await navigator.gpu.requestAdapter();\
const device = await adapter.requestDevice();\
Module.preinitializedWebGPUDevice = device;\
_kinc_internal_webgpu_initialized();\
})();";
emscripten_run_script(code);
#else
kickstart(argc, argv);
initialized = true;
#endif
emscripten_set_main_loop(drawfunc, 0, 1);
}

View File

@ -0,0 +1,15 @@
#include <kinc/threads/thread.h>
void kinc_thread_init(kinc_thread_t *t, void (*thread)(void *param), void *param) {}
void kinc_thread_wait_and_destroy(kinc_thread_t *thread) {}
bool kinc_thread_try_to_destroy(kinc_thread_t *thread) {
return false;
}
void kinc_threads_init() {}
void kinc_threads_quit() {}
void kinc_thread_sleep(int milliseconds) {}

View File

@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int nothing;
} kinc_thread_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,13 @@
#include <kinc/threads/threadlocal.h>
#include <string.h>
void kinc_thread_local_init(kinc_thread_local_t *local) {}
void kinc_thread_local_destroy(kinc_thread_local_t *local) {}
void *kinc_thread_local_get(kinc_thread_local_t *local) {
return NULL;
}
void kinc_thread_local_set(kinc_thread_local_t *local, void *data) {}

View File

@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int nothing;
} kinc_thread_local_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,57 @@
#include <kinc/video.h>
void kinc_video_init(kinc_video_t *video, const char *filename) {}
void kinc_video_destroy(kinc_video_t *video) {}
void kinc_video_play(kinc_video_t *video, bool loop) {}
void kinc_video_pause(kinc_video_t *video) {}
void kinc_video_stop(kinc_video_t *video) {}
int kinc_video_width(kinc_video_t *video) {
return 256;
}
int kinc_video_height(kinc_video_t *video) {
return 256;
}
kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) {
return NULL;
}
double kinc_video_duration(kinc_video_t *video) {
return 0.0;
}
double kinc_video_position(kinc_video_t *video) {
return 0.0;
}
bool kinc_video_finished(kinc_video_t *video) {
return false;
}
bool kinc_video_paused(kinc_video_t *video) {
return false;
}
void kinc_video_update(kinc_video_t *video, double time) {}
void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {}
void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) {}
void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {}
float samples[2] = {0};
float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) {
return samples;
}
bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) {
return true;
}

View File

@ -0,0 +1,27 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int nothing;
} kinc_video_impl_t;
typedef struct kinc_internal_video_sound_stream {
int nothing;
} kinc_internal_video_sound_stream_t;
void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency);
void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream);
void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count);
float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream);
bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,81 @@
#include <kinc/display.h>
#include <kinc/graphics4/graphics.h>
#include <kinc/window.h>
#include <string.h>
int kinc_internal_window_width = 0;
int kinc_internal_window_height = 0;
kinc_window_mode_t kinc_internal_window_mode = KINC_WINDOW_MODE_WINDOW;
int kinc_count_windows(void) {
return 1;
}
int kinc_window_x(int window_index) {
return 0;
}
int kinc_window_y(int window_index) {
return 0;
}
int kinc_window_width(int window_index) {
return kinc_internal_window_width;
}
int kinc_window_height(int window_index) {
return kinc_internal_window_height;
}
void kinc_window_resize(int window_index, int width, int height) {}
void kinc_window_move(int window_index, int x, int y) {}
void kinc_window_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) {
//**kinc_g4_changeFramebuffer(0, frame);
}
void kinc_window_change_features(int window_index, int features) {}
// In HTML5 fullscreen is activable only from user input.
void kinc_window_change_mode(int window_index, kinc_window_mode_t mode) {
if (mode == KINC_WINDOW_MODE_FULLSCREEN || mode == KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
if (kinc_internal_window_mode == KINC_WINDOW_MODE_FULLSCREEN || kinc_internal_window_mode == KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
kinc_internal_window_mode = mode;
return;
}
// TODO: call js Fullscreen API
kinc_internal_window_mode = mode;
}
else {
if (mode == kinc_internal_window_mode) {
return;
}
// TODO: call js Fullscreen API
kinc_internal_window_mode = mode;
}
}
void kinc_window_destroy(int window_index) {}
void kinc_window_show(int window_index) {}
void kinc_window_hide(int window_index) {}
// TODO: change browser title.
void kinc_window_set_title(int window_index, const char *title) {}
int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) {
return 0;
}
void kinc_window_set_resize_callback(int window_index, void (*callback)(int x, int y, void *data), void *data) {}
void kinc_window_set_ppi_changed_callback(int window_index, void (*callback)(int ppi, void *data), void *data) {}
void kinc_window_set_close_callback(int window, bool (*callback)(void *), void *data) {}
kinc_window_mode_t kinc_window_get_mode(int window_index) {
return kinc_internal_window_mode;
}

View File

@ -0,0 +1,8 @@
#pragma once
namespace Kore {
struct WindowData {
int width, height, mode;
WindowData();
};
}

View File

@ -0,0 +1,128 @@
#include "gamepad.h"
#include <kinc/input/gamepad.h>
#include <fcntl.h>
#include <linux/joystick.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
using namespace Kore;
namespace {
struct HIDGamepad {
HIDGamepad();
~HIDGamepad();
void init(int index);
void update();
int idx;
char gamepad_dev_name[256];
char name[384];
int file_descriptor;
bool connected;
js_event gamepadEvent;
void open();
void close();
void processEvent(js_event e);
};
HIDGamepad::HIDGamepad() {}
void HIDGamepad::init(int index) {
file_descriptor = -1;
connected = false;
gamepad_dev_name[0] = 0;
if (index >= 0 && index < 12) {
idx = index;
snprintf(gamepad_dev_name, sizeof(gamepad_dev_name), "/dev/input/js%d", idx);
open();
}
}
HIDGamepad::~HIDGamepad() {
close();
}
void HIDGamepad::open() {
file_descriptor = ::open(gamepad_dev_name, O_RDONLY | O_NONBLOCK);
if (file_descriptor < 0) {
connected = false;
}
else {
connected = true;
char buf[128];
if (ioctl(file_descriptor, JSIOCGNAME(sizeof(buf)), buf) < 0)
strncpy(buf, "Unknown", sizeof(buf));
snprintf(name, sizeof(name), "%s%s%s%s", buf, " (", gamepad_dev_name, ")");
kinc_internal_gamepad_trigger_connect(idx);
}
}
void HIDGamepad::close() {
if (connected) {
kinc_internal_gamepad_trigger_disconnect(idx);
::close(file_descriptor);
file_descriptor = -1;
connected = false;
}
}
void HIDGamepad::update() {
if (connected) {
while (read(file_descriptor, &gamepadEvent, sizeof(gamepadEvent)) > 0) {
processEvent(gamepadEvent);
}
}
}
void HIDGamepad::processEvent(js_event e) {
switch (e.type) {
case JS_EVENT_BUTTON:
kinc_internal_gamepad_trigger_button(idx, e.number, e.value);
break;
case JS_EVENT_AXIS: {
float value = e.number % 2 == 0 ? e.value : -e.value;
kinc_internal_gamepad_trigger_axis(idx, e.number, value / 32767.0f);
break;
}
default:
break;
}
}
HIDGamepad gamepads[KINC_GAMEPAD_MAX_COUNT];
}
void Kore::initHIDGamepads() {
for (int i = 0; i < KINC_GAMEPAD_MAX_COUNT; ++i) {
gamepads[i].init(i);
}
}
void Kore::updateHIDGamepads() {
for (int i = 0; i < KINC_GAMEPAD_MAX_COUNT; ++i) {
gamepads[i].update();
}
}
void Kore::closeHIDGamepads() {}
const char *kinc_gamepad_vendor(int gamepad) {
return "Linux gamepad";
}
const char *kinc_gamepad_product_name(int gamepad) {
return gamepad >= 0 && gamepad < KINC_GAMEPAD_MAX_COUNT ? gamepads[gamepad].name : "";
}
bool kinc_gamepad_connected(int gamepad) {
return gamepad >= 0 && gamepad < KINC_GAMEPAD_MAX_COUNT && gamepads[gamepad].connected;
}
void kinc_gamepad_rumble(int gamepad, float left, float right) {}

View File

@ -0,0 +1,7 @@
#pragma once
namespace Kore {
void initHIDGamepads();
void updateHIDGamepads();
void closeHIDGamepads();
}

View File

@ -0,0 +1,41 @@
#include "funcs.h"
#include <kinc/display.h>
void kinc_display_init() {
static bool display_initialized = false;
if (display_initialized) {
return;
}
kinc_linux_init_procs();
procs.display_init();
display_initialized = true;
}
kinc_display_mode_t kinc_display_available_mode(int display, int mode) {
return procs.display_available_mode(display, mode);
}
int kinc_display_count_available_modes(int display) {
return procs.display_count_available_modes(display);
}
bool kinc_display_available(int display) {
return procs.display_available(display);
}
const char *kinc_display_name(int display) {
return procs.display_name(display);
}
kinc_display_mode_t kinc_display_current_mode(int display) {
return procs.display_current_mode(display);
}
int kinc_primary_display(void) {
return procs.display_primary();
}
int kinc_count_displays(void) {
return procs.count_displays();
}

View File

@ -0,0 +1,67 @@
#pragma once
#include <kinc/display.h>
#include <kinc/global.h>
#include <kinc/window.h>
#ifdef KINC_EGL
#define EGL_NO_PLATFORM_SPECIFIC_TYPES
#include <EGL/egl.h>
#endif
#ifdef KINC_VULKAN
#include <vulkan/vulkan.h>
#endif
struct linux_procs {
bool (*handle_messages)(void);
void (*shutdown)(void);
void (*display_init)(void);
kinc_display_mode_t (*display_available_mode)(int display, int mode);
int (*display_count_available_modes)(int display);
bool (*display_available)(int display_index);
const char *(*display_name)(int display_index);
kinc_display_mode_t (*display_current_mode)(int display_index);
int (*display_primary)(void);
int (*count_displays)(void);
int (*window_create)(kinc_window_options_t *window_options, kinc_framebuffer_options_t *framebuffer_options);
void (*window_destroy)(int window_index);
int (*window_display)(int window_index);
void (*window_show)(int window_index);
void (*window_hide)(int window_index);
void (*window_set_title)(int window_index, const char *title);
void (*window_change_mode)(int window_index, kinc_window_mode_t mode);
kinc_window_mode_t (*window_get_mode)(int window_index);
void (*window_move)(int window_index, int x, int y);
void (*window_resize)(int window_index, int width, int height);
int (*window_x)(int window_index);
int (*window_y)(int window_index);
int (*window_width)(int window_index);
int (*window_height)(int window_index);
int (*count_windows)(void);
bool (*mouse_can_lock)(void);
bool (*mouse_is_locked)(void);
void (*mouse_lock)(int window);
void (*mouse_unlock)(void);
void (*mouse_show)(void);
void (*mouse_hide)(void);
void (*mouse_set_position)(int window, int x, int y);
void (*mouse_get_position)(int window, int *x, int *y);
void (*mouse_set_cursor)(int cursor);
void (*copy_to_clipboard)(const char *text);
#ifdef KINC_EGL
EGLDisplay (*egl_get_display)(void);
EGLNativeWindowType (*egl_get_native_window)(EGLDisplay display, EGLConfig config, int window_index);
#endif
#ifdef KINC_VULKAN
void (*vulkan_get_instance_extensions)(const char **extensions, int *count, int max);
VkResult (*vulkan_create_surface)(VkInstance instance, int window_index, VkSurfaceKHR *surface);
VkBool32 (*vulkan_get_physical_device_presentation_support)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
#endif
};
extern struct linux_procs procs;
void kinc_linux_init_procs();

View File

@ -0,0 +1,198 @@
#include "gamepad.h"
#include <kinc/input/gamepad.h>
#include <fcntl.h>
#include <libudev.h>
#include <linux/joystick.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
struct HIDGamepad {
int idx;
char gamepad_dev_name[256];
char name[385];
int file_descriptor;
bool connected;
struct js_event gamepadEvent;
};
static void HIDGamepad_open(struct HIDGamepad *pad) {
pad->file_descriptor = open(pad->gamepad_dev_name, O_RDONLY | O_NONBLOCK);
if (pad->file_descriptor < 0) {
pad->connected = false;
}
else {
pad->connected = true;
char buf[128];
if (ioctl(pad->file_descriptor, JSIOCGNAME(sizeof(buf)), buf) < 0) {
strncpy(buf, "Unknown", sizeof(buf));
}
snprintf(pad->name, sizeof(pad->name), "%s(%s)", buf, pad->gamepad_dev_name);
kinc_internal_gamepad_trigger_connect(pad->idx);
}
}
static void HIDGamepad_init(struct HIDGamepad *pad, int index) {
pad->file_descriptor = -1;
pad->connected = false;
pad->gamepad_dev_name[0] = 0;
if (index >= 0 && index < 12) {
pad->idx = index;
snprintf(pad->gamepad_dev_name, sizeof(pad->gamepad_dev_name), "/dev/input/js%d", pad->idx);
HIDGamepad_open(pad);
}
}
static void HIDGamepad_close(struct HIDGamepad *pad) {
if (pad->connected) {
kinc_internal_gamepad_trigger_disconnect(pad->idx);
close(pad->file_descriptor);
pad->file_descriptor = -1;
pad->connected = false;
}
}
void HIDGamepad_processEvent(struct HIDGamepad *pad, struct js_event e) {
switch (e.type) {
case JS_EVENT_BUTTON:
kinc_internal_gamepad_trigger_button(pad->idx, e.number, e.value);
break;
case JS_EVENT_AXIS: {
float value = e.number % 2 == 0 ? e.value : -e.value;
kinc_internal_gamepad_trigger_axis(pad->idx, e.number, value / 32767.0f);
break;
}
default:
break;
}
}
void HIDGamepad_update(struct HIDGamepad *pad) {
if (pad->connected) {
while (read(pad->file_descriptor, &pad->gamepadEvent, sizeof(pad->gamepadEvent)) > 0) {
HIDGamepad_processEvent(pad, pad->gamepadEvent);
}
}
}
struct HIDGamepadUdevHelper {
struct udev *udevPtr;
struct udev_monitor *udevMonitorPtr;
int udevMonitorFD;
};
static struct HIDGamepadUdevHelper udev_helper;
static struct HIDGamepad gamepads[KINC_GAMEPAD_MAX_COUNT];
static void HIDGamepadUdevHelper_openOrCloseGamepad(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) {
const char *action = udev_device_get_action(dev);
if (!action)
action = "add";
const char *joystickDevnodeName = strstr(udev_device_get_devnode(dev), "js");
if (joystickDevnodeName) {
int joystickDevnodeIndex;
sscanf(joystickDevnodeName, "js%d", &joystickDevnodeIndex);
if (!strcmp(action, "add")) {
HIDGamepad_open(&gamepads[joystickDevnodeIndex]);
}
if (!strcmp(action, "remove")) {
HIDGamepad_close(&gamepads[joystickDevnodeIndex]);
}
}
}
static void HIDGamepadUdevHelper_processDevice(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) {
if (dev) {
if (udev_device_get_devnode(dev))
HIDGamepadUdevHelper_openOrCloseGamepad(helper, dev);
udev_device_unref(dev);
}
}
static void HIDGamepadUdevHelper_init(struct HIDGamepadUdevHelper *helper) {
struct udev *udevPtrNew = udev_new();
// enumerate
struct udev_enumerate *enumerate = udev_enumerate_new(udevPtrNew);
udev_enumerate_add_match_subsystem(enumerate, "input");
udev_enumerate_scan_devices(enumerate);
struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *entry;
udev_list_entry_foreach(entry, devices) {
const char *path = udev_list_entry_get_name(entry);
struct udev_device *dev = udev_device_new_from_syspath(udevPtrNew, path);
HIDGamepadUdevHelper_processDevice(helper, dev);
}
udev_enumerate_unref(enumerate);
// setup mon
helper->udevMonitorPtr = udev_monitor_new_from_netlink(udevPtrNew, "udev");
udev_monitor_filter_add_match_subsystem_devtype(helper->udevMonitorPtr, "input", NULL);
udev_monitor_enable_receiving(helper->udevMonitorPtr);
helper->udevMonitorFD = udev_monitor_get_fd(helper->udevMonitorPtr);
helper->udevPtr = udevPtrNew;
}
static void HIDGamepadUdevHelper_update(struct HIDGamepadUdevHelper *helper) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(helper->udevMonitorFD, &fds);
if (FD_ISSET(helper->udevMonitorFD, &fds)) {
struct udev_device *dev = udev_monitor_receive_device(helper->udevMonitorPtr);
HIDGamepadUdevHelper_processDevice(helper, dev);
}
}
static void HIDGamepadUdevHelper_close(struct HIDGamepadUdevHelper *helper) {
udev_unref(helper->udevPtr);
}
void kinc_linux_initHIDGamepads() {
for (int i = 0; i < KINC_GAMEPAD_MAX_COUNT; ++i) {
HIDGamepad_init(&gamepads[i], i);
}
HIDGamepadUdevHelper_init(&udev_helper);
}
void kinc_linux_updateHIDGamepads() {
HIDGamepadUdevHelper_update(&udev_helper);
for (int i = 0; i < KINC_GAMEPAD_MAX_COUNT; ++i) {
HIDGamepad_update(&gamepads[i]);
}
}
void kinc_linux_closeHIDGamepads() {
HIDGamepadUdevHelper_close(&udev_helper);
}
const char *kinc_gamepad_vendor(int gamepad) {
return "Linux gamepad";
}
const char *kinc_gamepad_product_name(int gamepad) {
return gamepad >= 0 && gamepad < KINC_GAMEPAD_MAX_COUNT ? gamepads[gamepad].name : "";
}
bool kinc_gamepad_connected(int gamepad) {
return gamepad >= 0 && gamepad < KINC_GAMEPAD_MAX_COUNT && gamepads[gamepad].connected;
}
void kinc_gamepad_rumble(int gamepad, float left, float right) {}

View File

@ -0,0 +1,5 @@
#pragma once
void kinc_linux_initHIDGamepads();
void kinc_linux_updateHIDGamepads();
void kinc_linux_closeHIDGamepads();

View File

@ -0,0 +1,161 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE // memfd_create and mkostemp
#endif
#include "funcs.h"
#include <dlfcn.h>
#include <stdio.h>
static void load_lib(void **lib, const char *name) {
char libname[64];
sprintf(libname, "lib%s.so", name);
*lib = dlopen(libname, RTLD_LAZY);
if (*lib != NULL) {
return;
}
// Ubuntu and Fedora only ship libFoo.so.major by default, so look for those.
for (int i = 0; i < 10; i++) {
sprintf(libname, "lib%s.so.%i", name, i);
*lib = dlopen(libname, RTLD_LAZY);
if (*lib != NULL) {
return;
}
}
}
#ifndef KINC_NO_WAYLAND
#include "wayland/display.c.h"
#include "wayland/system.c.h"
#include "wayland/window.c.h"
#endif
#ifndef KINC_NO_X11
#include "x11/display.c.h"
#include "x11/system.c.h"
#include "x11/window.c.h"
#endif
struct linux_procs procs = {0};
void kinc_linux_init_procs() {
if (procs.window_create != NULL) {
return;
}
#ifndef KINC_NO_WAYLAND
if (kinc_wayland_init()) {
procs.handle_messages = kinc_wayland_handle_messages;
procs.shutdown = kinc_wayland_shutdown;
procs.window_create = kinc_wayland_window_create;
procs.window_width = kinc_wayland_window_width;
procs.window_height = kinc_wayland_window_height;
procs.window_x = kinc_wayland_window_x;
procs.window_y = kinc_wayland_window_y;
procs.window_destroy = kinc_wayland_window_destroy;
procs.window_change_mode = kinc_wayland_window_change_mode;
procs.window_get_mode = kinc_wayland_window_get_mode;
procs.window_set_title = kinc_wayland_window_set_title;
procs.window_display = kinc_wayland_window_display;
procs.window_move = kinc_wayland_window_move;
procs.window_resize = kinc_wayland_window_resize;
procs.window_show = kinc_wayland_window_show;
procs.window_hide = kinc_wayland_window_hide;
procs.count_windows = kinc_wayland_count_windows;
procs.mouse_can_lock = kinc_wl_mouse_can_lock;
procs.mouse_lock = kinc_wl_mouse_lock;
procs.mouse_unlock = kinc_wl_mouse_unlock;
procs.mouse_show = kinc_wl_mouse_show;
procs.mouse_hide = kinc_wl_mouse_hide;
procs.mouse_set_position = kinc_wl_mouse_set_position;
procs.mouse_get_position = kinc_wl_mouse_get_position;
procs.mouse_set_cursor = kinc_wl_mouse_set_cursor;
procs.display_init = kinc_wayland_display_init;
procs.display_available = kinc_wayland_display_available;
procs.display_available_mode = kinc_wayland_display_available_mode;
procs.display_count_available_modes = kinc_wayland_display_count_available_modes;
procs.display_current_mode = kinc_wayland_display_current_mode;
procs.display_name = kinc_wayland_display_name;
procs.display_primary = kinc_wayland_display_primary;
procs.count_displays = kinc_wayland_count_displays;
procs.copy_to_clipboard = kinc_wayland_copy_to_clipboard;
#ifdef KINC_EGL
procs.egl_get_display = kinc_wayland_egl_get_display;
procs.egl_get_native_window = kinc_wayland_egl_get_native_window;
#endif
#ifdef KINC_VULKAN
procs.vulkan_create_surface = kinc_wayland_vulkan_create_surface;
procs.vulkan_get_instance_extensions = kinc_wayland_vulkan_get_instance_extensions;
procs.vulkan_get_physical_device_presentation_support = kinc_wayland_vulkan_get_physical_device_presentation_support;
#endif
}
else
#endif
#ifndef KINC_NO_X11
if (kinc_x11_init()) {
procs.handle_messages = kinc_x11_handle_messages;
procs.shutdown = kinc_x11_shutdown;
procs.window_create = kinc_x11_window_create;
procs.window_width = kinc_x11_window_width;
procs.window_height = kinc_x11_window_height;
procs.window_x = kinc_x11_window_x;
procs.window_y = kinc_x11_window_y;
procs.window_destroy = kinc_x11_window_destroy;
procs.window_change_mode = kinc_x11_window_change_mode;
procs.window_get_mode = kinc_x11_window_get_mode;
procs.window_set_title = kinc_x11_window_set_title;
procs.window_display = kinc_x11_window_display;
procs.window_move = kinc_x11_window_move;
procs.window_resize = kinc_x11_window_resize;
procs.window_show = kinc_x11_window_show;
procs.window_hide = kinc_x11_window_hide;
procs.count_windows = kinc_x11_count_windows;
procs.display_init = kinc_x11_display_init;
procs.display_available = kinc_x11_display_available;
procs.display_available_mode = kinc_x11_display_available_mode;
procs.display_count_available_modes = kinc_x11_display_count_available_modes;
procs.display_current_mode = kinc_x11_display_current_mode;
procs.display_name = kinc_x11_display_name;
procs.display_primary = kinc_x11_display_primary;
procs.count_displays = kinc_x11_count_displays;
procs.mouse_can_lock = kinc_x11_mouse_can_lock;
procs.mouse_lock = kinc_x11_mouse_lock;
procs.mouse_unlock = kinc_x11_mouse_unlock;
procs.mouse_show = kinc_x11_mouse_show;
procs.mouse_hide = kinc_x11_mouse_hide;
procs.mouse_set_position = kinc_x11_mouse_set_position;
procs.mouse_get_position = kinc_x11_mouse_get_position;
procs.mouse_set_cursor = kinc_x11_mouse_set_cursor;
procs.copy_to_clipboard = kinc_x11_copy_to_clipboard;
#ifdef KINC_EGL
procs.egl_get_display = kinc_x11_egl_get_display;
procs.egl_get_native_window = kinc_x11_egl_get_native_window;
#endif
#ifdef KINC_VULKAN
procs.vulkan_create_surface = kinc_x11_vulkan_create_surface;
procs.vulkan_get_instance_extensions = kinc_x11_vulkan_get_instance_extensions;
procs.vulkan_get_physical_device_presentation_support = kinc_x11_vulkan_get_physical_device_presentation_support;
#endif
}
else
#endif
{
kinc_log(KINC_LOG_LEVEL_ERROR, "Neither wayland nor X11 found.");
exit(1);
}
}
#include "display.c.h"
#ifndef __FreeBSD__
#include "gamepad.c.h"
#endif
#include "mouse.c.h"
#include "sound.c.h"
#include "system.c.h"
#include "video.c.h"
#include "window.c.h"

View File

@ -0,0 +1,36 @@
#include "funcs.h"
#include <kinc/input/mouse.h>
void kinc_internal_mouse_lock(int window) {
procs.mouse_lock(window);
}
void kinc_internal_mouse_unlock() {
procs.mouse_unlock();
}
bool kinc_mouse_can_lock(void) {
return true;
}
bool _mouseHidden = false;
void kinc_mouse_show() {
procs.mouse_show();
}
void kinc_mouse_hide() {
procs.mouse_hide();
}
void kinc_mouse_set_cursor(int cursorIndex) {
procs.mouse_set_cursor(cursorIndex);
}
void kinc_mouse_set_position(int window, int x, int y) {
procs.mouse_set_position(window, x, y);
}
void kinc_mouse_get_position(int window, int *x, int *y) {
procs.mouse_get_position(window, x, y);
}

View File

@ -0,0 +1,233 @@
#include <kinc/audio2/audio.h>
#include <alsa/asoundlib.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
// apt-get install libasound2-dev
kinc_a2_buffer_t a2_buffer;
pthread_t threadid;
bool audioRunning = false;
snd_pcm_t *playback_handle;
short buf[4096 * 4];
static unsigned int samples_per_second = 44100;
uint32_t kinc_a2_samples_per_second(void) {
return samples_per_second;
}
void copySample(void *buffer) {
float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location];
float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location];
a2_buffer.read_location += 1;
if (a2_buffer.read_location >= a2_buffer.data_size) {
a2_buffer.read_location = 0;
}
((int16_t *)buffer)[0] = (int16_t)(left_value * 32767);
((int16_t *)buffer)[1] = (int16_t)(right_value * 32767);
}
int playback_callback(snd_pcm_sframes_t nframes) {
int err = 0;
if (kinc_a2_internal_callback(&a2_buffer, nframes)) {
int ni = 0;
while (ni < nframes) {
int i = 0;
for (; ni < nframes && i < 4096 * 2; ++i, ++ni) {
copySample(&buf[i * 2]);
}
int err2;
if ((err2 = snd_pcm_writei(playback_handle, buf, i)) < 0) {
fprintf(stderr, "write failed (%s)\n", snd_strerror(err2));
}
err += err2;
}
}
return err;
}
bool tryToRecover(snd_pcm_t *handle, int errorCode) {
switch (-errorCode) {
case EINTR:
case EPIPE:
case ESPIPE:
#if !defined(__FreeBSD__)
case ESTRPIPE:
#endif
{
int recovered = snd_pcm_recover(playback_handle, errorCode, 1);
if (recovered != 0) {
fprintf(stderr, "unable to recover from ALSA error code=%i\n", errorCode);
return false;
}
else {
fprintf(stdout, "recovered from ALSA error code=%i\n", errorCode);
return true;
}
}
default:
fprintf(stderr, "unhandled ALSA error code=%i\n", errorCode);
return false;
}
}
void *doAudio(void *arg) {
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
snd_pcm_sframes_t frames_to_deliver;
int err;
if ((err = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf(stderr, "cannot open audio device default (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_hw_params_any(playback_handle, hw_params)) < 0) {
fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err));
return NULL;
}
int dir = 0;
if ((err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &samples_per_second, &dir)) < 0) {
fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2)) < 0) {
fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err));
return NULL;
}
snd_pcm_uframes_t bufferSize = samples_per_second / 8;
if (((err = snd_pcm_hw_params_set_buffer_size(playback_handle, hw_params, bufferSize)) < 0 &&
(snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &bufferSize)) < 0)) {
fprintf(stderr, "cannot set buffer size (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_hw_params(playback_handle, hw_params)) < 0) {
fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err));
return NULL;
}
snd_pcm_hw_params_free(hw_params);
/* tell ALSA to wake us up whenever 4096 or more frames
of playback data can be delivered. Also, tell
ALSA that we'll start the device ourselves.
*/
if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {
fprintf(stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_sw_params_current(playback_handle, sw_params)) < 0) {
fprintf(stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_sw_params_set_avail_min(playback_handle, sw_params, 4096)) < 0) {
fprintf(stderr, "cannot set minimum available count (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_sw_params_set_start_threshold(playback_handle, sw_params, 0U)) < 0) {
fprintf(stderr, "cannot set start mode (%s)\n", snd_strerror(err));
return NULL;
}
if ((err = snd_pcm_sw_params(playback_handle, sw_params)) < 0) {
fprintf(stderr, "cannot set software parameters (%s)\n", snd_strerror(err));
return NULL;
}
/* the interface will interrupt the kernel every 4096 frames, and ALSA
will wake up this program very soon after that.
*/
if ((err = snd_pcm_prepare(playback_handle)) < 0) {
fprintf(stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err));
return NULL;
}
while (audioRunning) {
/* wait till the interface is ready for data, or 1 second
has elapsed.
*/
if ((err = snd_pcm_wait(playback_handle, 1000)) < 0) {
fprintf(stderr, "poll failed (%s)\n", strerror(errno));
break;
}
/* find out how much space is available for playback data */
if ((frames_to_deliver = snd_pcm_avail_update(playback_handle)) < 0) {
if (!tryToRecover(playback_handle, frames_to_deliver)) {
// break;
}
}
else {
// frames_to_deliver = frames_to_deliver > 4096 ? 4096 : frames_to_deliver;
/* deliver the data */
int delivered = playback_callback(frames_to_deliver);
if (delivered != frames_to_deliver) {
fprintf(stderr, "playback callback failed (delivered %i / %ld frames)\n", delivered, frames_to_deliver);
// break;
}
}
}
snd_pcm_close(playback_handle);
return NULL;
}
static bool initialized = false;
void kinc_a2_init() {
if (initialized) {
return;
}
kinc_a2_internal_init();
initialized = true;
a2_buffer.read_location = 0;
a2_buffer.write_location = 0;
a2_buffer.data_size = 128 * 1024;
a2_buffer.channel_count = 2;
a2_buffer.channels[0] = (float *)malloc(a2_buffer.data_size * sizeof(float));
a2_buffer.channels[1] = (float *)malloc(a2_buffer.data_size * sizeof(float));
audioRunning = true;
pthread_create(&threadid, NULL, &doAudio, NULL);
}
void kinc_a2_update() {}
void kinc_a2_shutdown() {
audioRunning = false;
}

View File

@ -0,0 +1,433 @@
#include "kinc/graphics4/graphics.h"
#include <kinc/display.h>
#include <kinc/input/gamepad.h>
#include <kinc/input/keyboard.h>
#include <kinc/input/mouse.h>
#include <kinc/input/pen.h>
#include <kinc/log.h>
#include <kinc/system.h>
#include <kinc/video.h>
#include <kinc/window.h>
#ifndef __FreeBSD__
#include "gamepad.h"
#endif
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "funcs.h"
bool kinc_internal_handle_messages() {
if (!procs.handle_messages()) {
return false;
}
#ifndef __FreeBSD__
kinc_linux_updateHIDGamepads();
#endif // TODO: add #else with proper call to FreeBSD backend impl
return true;
}
const char *kinc_system_id() {
return "Linux";
}
void kinc_set_keep_screen_on(bool on) {}
void kinc_keyboard_show() {}
void kinc_keyboard_hide() {}
bool kinc_keyboard_active() {
return true;
}
void kinc_load_url(const char *url) {
#define MAX_COMMAND_BUFFER_SIZE 256
#define HTTP "http://"
#define HTTPS "https://"
if (strncmp(url, HTTP, sizeof(HTTP) - 1) == 0 || strncmp(url, HTTPS, sizeof(HTTPS) - 1) == 0) {
char openUrlCommand[MAX_COMMAND_BUFFER_SIZE];
snprintf(openUrlCommand, MAX_COMMAND_BUFFER_SIZE, "xdg-open %s", url);
int err = system(openUrlCommand);
if (err != 0) {
kinc_log(KINC_LOG_LEVEL_WARNING, "Error opening url %s", url);
}
}
#undef HTTPS
#undef HTTP
#undef MAX_COMMAND_BUFFER_SIZE
}
void kinc_vibrate(int ms) {}
const char *kinc_language() {
return "en";
}
static char save[2000];
static bool saveInitialized = false;
const char *kinc_internal_save_path() {
// first check for an existing directory in $HOME
// if one exists, use it, else create one in $XDG_DATA_HOME
// See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
if (!saveInitialized) {
const char *homedir;
if ((homedir = getenv("HOME")) == NULL) {
homedir = getpwuid(getuid())->pw_dir;
}
strcpy(save, homedir);
strcat(save, "/.");
strcat(save, kinc_application_name());
strcat(save, "/");
struct stat st;
if (stat(save, &st) == 0) {
// use existing folder in $HOME
}
else {
// use XDG folder
const char *data_home;
if ((data_home = getenv("XDG_DATA_HOME")) == NULL) {
// $XDG_DATA_HOME is not defined, fall back to the default, $HOME/.local/share
strcpy(save, homedir);
strcat(save, "/.local/share/");
}
else {
// use $XDG_DATA_HOME
strcpy(save, data_home);
if (data_home[strlen(data_home) - 1] != '/') {
strcat(save, "/");
}
}
strcat(save, kinc_application_name());
strcat(save, "/");
int res = mkdir(save, 0700);
if (res != 0 && errno != EEXIST) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Could not create save directory '%s'. Error %d", save, errno);
}
}
saveInitialized = true;
}
return save;
}
static const char *videoFormats[] = {"ogv", NULL};
const char **kinc_video_formats() {
return videoFormats;
}
#include <sys/time.h>
#include <time.h>
double kinc_frequency(void) {
return 1000000.0;
}
static struct timeval start;
kinc_ticks_t kinc_timestamp(void) {
struct timeval now;
gettimeofday(&now, NULL);
now.tv_sec -= start.tv_sec;
now.tv_usec -= start.tv_usec;
return (kinc_ticks_t)now.tv_sec * 1000000 + (kinc_ticks_t)now.tv_usec;
}
void kinc_login() {}
void kinc_unlock_achievement(int id) {}
void kinc_linux_init_procs();
#ifdef KINC_KONG
void kong_init(void);
#endif
int kinc_init(const char *name, int width, int height, kinc_window_options_t *win, kinc_framebuffer_options_t *frame) {
#ifndef __FreeBSD__
kinc_linux_initHIDGamepads();
#endif // TODO: add #else with proper call to FreeBSD backend impl
gettimeofday(&start, NULL);
kinc_linux_init_procs();
kinc_display_init();
kinc_set_application_name(name);
kinc_g4_internal_init();
kinc_window_options_t defaultWin;
if (win == NULL) {
kinc_window_options_set_defaults(&defaultWin);
win = &defaultWin;
}
kinc_framebuffer_options_t defaultFrame;
if (frame == NULL) {
kinc_framebuffer_options_set_defaults(&defaultFrame);
frame = &defaultFrame;
}
win->width = width;
win->height = height;
if (win->title == NULL) {
win->title = name;
}
int window = kinc_window_create(win, frame);
#ifdef KINC_KONG
kong_init();
#endif
return window;
}
void kinc_internal_shutdown() {
kinc_g4_internal_destroy();
#ifndef __FreeBSD__
kinc_linux_closeHIDGamepads();
#endif // TODO: add #else with proper call to FreeBSD backend impl
procs.shutdown();
kinc_internal_shutdown_callback();
}
#ifndef KINC_NO_MAIN
int main(int argc, char **argv) {
return kickstart(argc, argv);
}
#endif
void kinc_copy_to_clipboard(const char *text) {
procs.copy_to_clipboard(text);
}
static int parse_number_at_end_of_line(char *line) {
char *end = &line[strlen(line) - 2];
int num = 0;
int multi = 1;
while (*end >= '0' && *end <= '9') {
num += (*end - '0') * multi;
multi *= 10;
--end;
}
return num;
}
int kinc_cpu_cores(void) {
char line[1024];
FILE *file = fopen("/proc/cpuinfo", "r");
if (file != NULL) {
int cores[1024];
memset(cores, 0, sizeof(cores));
int cpu_count = 0;
int physical_id = -1;
int per_cpu_cores = -1;
int processor_count = 0;
while (fgets(line, sizeof(line), file)) {
if (strncmp(line, "processor", 9) == 0) {
++processor_count;
if (physical_id >= 0 && per_cpu_cores > 0) {
if (physical_id + 1 > cpu_count) {
cpu_count = physical_id + 1;
}
cores[physical_id] = per_cpu_cores;
physical_id = -1;
per_cpu_cores = -1;
}
}
else if (strncmp(line, "physical id", 11) == 0) {
physical_id = parse_number_at_end_of_line(line);
}
else if (strncmp(line, "cpu cores", 9) == 0) {
per_cpu_cores = parse_number_at_end_of_line(line);
}
}
fclose(file);
if (physical_id >= 0 && per_cpu_cores > 0) {
if (physical_id + 1 > cpu_count) {
cpu_count = physical_id + 1;
}
cores[physical_id] = per_cpu_cores;
}
int proper_cpu_count = 0;
for (int i = 0; i < cpu_count; ++i) {
proper_cpu_count += cores[i];
}
if (proper_cpu_count > 0) {
return proper_cpu_count;
}
else {
return processor_count == 0 ? 1 : processor_count;
}
}
else {
return 1;
};
}
int kinc_hardware_threads(void) {
#ifndef __FreeBSD__
return sysconf(_SC_NPROCESSORS_ONLN);
#else
return kinc_cpu_cores();
#endif
}
#include <xkbcommon/xkbcommon.h>
int xkb_to_kinc(xkb_keysym_t symbol) {
#define KEY(xkb, kinc) \
case xkb: \
return kinc;
switch (symbol) {
KEY(XKB_KEY_Right, KINC_KEY_RIGHT)
KEY(XKB_KEY_Left, KINC_KEY_LEFT)
KEY(XKB_KEY_Up, KINC_KEY_UP)
KEY(XKB_KEY_Down, KINC_KEY_DOWN)
KEY(XKB_KEY_space, KINC_KEY_SPACE)
KEY(XKB_KEY_BackSpace, KINC_KEY_BACKSPACE)
KEY(XKB_KEY_Tab, KINC_KEY_TAB)
KEY(XKB_KEY_Return, KINC_KEY_RETURN)
KEY(XKB_KEY_Shift_L, KINC_KEY_SHIFT)
KEY(XKB_KEY_Shift_R, KINC_KEY_SHIFT)
KEY(XKB_KEY_Control_L, KINC_KEY_CONTROL)
KEY(XKB_KEY_Control_R, KINC_KEY_CONTROL)
KEY(XKB_KEY_Alt_L, KINC_KEY_ALT)
KEY(XKB_KEY_Alt_R, KINC_KEY_ALT)
KEY(XKB_KEY_Delete, KINC_KEY_DELETE)
KEY(XKB_KEY_comma, KINC_KEY_COMMA)
KEY(XKB_KEY_period, KINC_KEY_PERIOD)
KEY(XKB_KEY_bracketleft, KINC_KEY_OPEN_BRACKET)
KEY(XKB_KEY_bracketright, KINC_KEY_CLOSE_BRACKET)
KEY(XKB_KEY_braceleft, KINC_KEY_OPEN_CURLY_BRACKET)
KEY(XKB_KEY_braceright, KINC_KEY_CLOSE_CURLY_BRACKET)
KEY(XKB_KEY_parenleft, KINC_KEY_OPEN_PAREN)
KEY(XKB_KEY_parenright, KINC_KEY_CLOSE_PAREN)
KEY(XKB_KEY_backslash, KINC_KEY_BACK_SLASH)
KEY(XKB_KEY_apostrophe, KINC_KEY_QUOTE)
KEY(XKB_KEY_colon, KINC_KEY_COLON)
KEY(XKB_KEY_semicolon, KINC_KEY_SEMICOLON)
KEY(XKB_KEY_minus, KINC_KEY_HYPHEN_MINUS)
KEY(XKB_KEY_underscore, KINC_KEY_UNDERSCORE)
KEY(XKB_KEY_slash, KINC_KEY_SLASH)
KEY(XKB_KEY_bar, KINC_KEY_PIPE)
KEY(XKB_KEY_question, KINC_KEY_QUESTIONMARK)
KEY(XKB_KEY_less, KINC_KEY_LESS_THAN)
KEY(XKB_KEY_greater, KINC_KEY_GREATER_THAN)
KEY(XKB_KEY_asterisk, KINC_KEY_ASTERISK)
KEY(XKB_KEY_ampersand, KINC_KEY_AMPERSAND)
KEY(XKB_KEY_asciicircum, KINC_KEY_CIRCUMFLEX)
KEY(XKB_KEY_percent, KINC_KEY_PERCENT)
KEY(XKB_KEY_dollar, KINC_KEY_DOLLAR)
KEY(XKB_KEY_numbersign, KINC_KEY_HASH)
KEY(XKB_KEY_at, KINC_KEY_AT)
KEY(XKB_KEY_exclam, KINC_KEY_EXCLAMATION)
KEY(XKB_KEY_equal, KINC_KEY_EQUALS)
KEY(XKB_KEY_plus, KINC_KEY_ADD)
KEY(XKB_KEY_quoteleft, KINC_KEY_BACK_QUOTE)
KEY(XKB_KEY_quotedbl, KINC_KEY_DOUBLE_QUOTE)
KEY(XKB_KEY_asciitilde, KINC_KEY_TILDE)
KEY(XKB_KEY_Pause, KINC_KEY_PAUSE)
KEY(XKB_KEY_Scroll_Lock, KINC_KEY_SCROLL_LOCK)
KEY(XKB_KEY_Home, KINC_KEY_HOME)
KEY(XKB_KEY_Page_Up, KINC_KEY_PAGE_UP)
KEY(XKB_KEY_Page_Down, KINC_KEY_PAGE_DOWN)
KEY(XKB_KEY_End, KINC_KEY_END)
KEY(XKB_KEY_Insert, KINC_KEY_INSERT)
KEY(XKB_KEY_KP_Enter, KINC_KEY_RETURN)
KEY(XKB_KEY_KP_Multiply, KINC_KEY_MULTIPLY)
KEY(XKB_KEY_KP_Add, KINC_KEY_ADD)
KEY(XKB_KEY_KP_Subtract, KINC_KEY_SUBTRACT)
KEY(XKB_KEY_KP_Decimal, KINC_KEY_DECIMAL)
KEY(XKB_KEY_KP_Divide, KINC_KEY_DIVIDE)
KEY(XKB_KEY_KP_0, KINC_KEY_NUMPAD_0)
KEY(XKB_KEY_KP_1, KINC_KEY_NUMPAD_1)
KEY(XKB_KEY_KP_2, KINC_KEY_NUMPAD_2)
KEY(XKB_KEY_KP_3, KINC_KEY_NUMPAD_3)
KEY(XKB_KEY_KP_4, KINC_KEY_NUMPAD_4)
KEY(XKB_KEY_KP_5, KINC_KEY_NUMPAD_5)
KEY(XKB_KEY_KP_6, KINC_KEY_NUMPAD_6)
KEY(XKB_KEY_KP_7, KINC_KEY_NUMPAD_7)
KEY(XKB_KEY_KP_8, KINC_KEY_NUMPAD_8)
KEY(XKB_KEY_KP_9, KINC_KEY_NUMPAD_9)
KEY(XKB_KEY_KP_Insert, KINC_KEY_INSERT)
KEY(XKB_KEY_KP_Delete, KINC_KEY_DELETE)
KEY(XKB_KEY_KP_End, KINC_KEY_END)
KEY(XKB_KEY_KP_Home, KINC_KEY_HOME)
KEY(XKB_KEY_KP_Left, KINC_KEY_LEFT)
KEY(XKB_KEY_KP_Up, KINC_KEY_UP)
KEY(XKB_KEY_KP_Right, KINC_KEY_RIGHT)
KEY(XKB_KEY_KP_Down, KINC_KEY_DOWN)
KEY(XKB_KEY_KP_Page_Up, KINC_KEY_PAGE_UP)
KEY(XKB_KEY_KP_Page_Down, KINC_KEY_PAGE_DOWN)
KEY(XKB_KEY_Menu, KINC_KEY_CONTEXT_MENU)
KEY(XKB_KEY_a, KINC_KEY_A)
KEY(XKB_KEY_b, KINC_KEY_B)
KEY(XKB_KEY_c, KINC_KEY_C)
KEY(XKB_KEY_d, KINC_KEY_D)
KEY(XKB_KEY_e, KINC_KEY_E)
KEY(XKB_KEY_f, KINC_KEY_F)
KEY(XKB_KEY_g, KINC_KEY_G)
KEY(XKB_KEY_h, KINC_KEY_H)
KEY(XKB_KEY_i, KINC_KEY_I)
KEY(XKB_KEY_j, KINC_KEY_J)
KEY(XKB_KEY_k, KINC_KEY_K)
KEY(XKB_KEY_l, KINC_KEY_L)
KEY(XKB_KEY_m, KINC_KEY_M)
KEY(XKB_KEY_n, KINC_KEY_N)
KEY(XKB_KEY_o, KINC_KEY_O)
KEY(XKB_KEY_p, KINC_KEY_P)
KEY(XKB_KEY_q, KINC_KEY_Q)
KEY(XKB_KEY_r, KINC_KEY_R)
KEY(XKB_KEY_s, KINC_KEY_S)
KEY(XKB_KEY_t, KINC_KEY_T)
KEY(XKB_KEY_u, KINC_KEY_U)
KEY(XKB_KEY_v, KINC_KEY_V)
KEY(XKB_KEY_w, KINC_KEY_W)
KEY(XKB_KEY_x, KINC_KEY_X)
KEY(XKB_KEY_y, KINC_KEY_Y)
KEY(XKB_KEY_z, KINC_KEY_Z)
KEY(XKB_KEY_1, KINC_KEY_1)
KEY(XKB_KEY_2, KINC_KEY_2)
KEY(XKB_KEY_3, KINC_KEY_3)
KEY(XKB_KEY_4, KINC_KEY_4)
KEY(XKB_KEY_5, KINC_KEY_5)
KEY(XKB_KEY_6, KINC_KEY_6)
KEY(XKB_KEY_7, KINC_KEY_7)
KEY(XKB_KEY_8, KINC_KEY_8)
KEY(XKB_KEY_9, KINC_KEY_9)
KEY(XKB_KEY_0, KINC_KEY_0)
KEY(XKB_KEY_Escape, KINC_KEY_ESCAPE)
KEY(XKB_KEY_F1, KINC_KEY_F1)
KEY(XKB_KEY_F2, KINC_KEY_F2)
KEY(XKB_KEY_F3, KINC_KEY_F3)
KEY(XKB_KEY_F4, KINC_KEY_F4)
KEY(XKB_KEY_F5, KINC_KEY_F5)
KEY(XKB_KEY_F6, KINC_KEY_F6)
KEY(XKB_KEY_F7, KINC_KEY_F7)
KEY(XKB_KEY_F8, KINC_KEY_F8)
KEY(XKB_KEY_F9, KINC_KEY_F9)
KEY(XKB_KEY_F10, KINC_KEY_F10)
KEY(XKB_KEY_F11, KINC_KEY_F11)
KEY(XKB_KEY_F12, KINC_KEY_F12)
default:
return KINC_KEY_UNKNOWN;
}
#undef KEY
}

View File

@ -0,0 +1,59 @@
#include <kinc/video.h>
#if !defined(KINC_VIDEO_GSTREAMER)
void kinc_video_init(kinc_video_t *video, const char *filename) {}
void kinc_video_destroy(kinc_video_t *video) {}
void kinc_video_play(kinc_video_t *video, bool loop) {}
void kinc_video_pause(kinc_video_t *video) {}
void kinc_video_stop(kinc_video_t *video) {}
int kinc_video_width(kinc_video_t *video) {
return 256;
}
int kinc_video_height(kinc_video_t *video) {
return 256;
}
kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) {
return NULL;
}
double kinc_video_duration(kinc_video_t *video) {
return 0.0;
}
double kinc_video_position(kinc_video_t *video) {
return 0.0;
}
bool kinc_video_finished(kinc_video_t *video) {
return false;
}
bool kinc_video_paused(kinc_video_t *video) {
return false;
}
void kinc_video_update(kinc_video_t *video, double time) {}
void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {}
void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) {}
void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {}
static float samples[2] = {0};
float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) {
return samples;
}
bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) {
return true;
}
#endif

View File

@ -0,0 +1,31 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#if defined(KINC_VIDEO_GSTREAMER)
#include <kinc/backend/video_gstreamer.h>
#else
typedef struct {
int nothing;
} kinc_video_impl_t;
typedef struct kinc_internal_video_sound_stream {
int nothing;
} kinc_internal_video_sound_stream_t;
void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency);
void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream);
void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count);
float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream);
bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,52 @@
#include "wayland.h"
void kinc_wayland_display_init(void) {
// This is a no-op because displays are already registered in kinc_wayland_init,
// which should be called before this function is ever invoked
}
int kinc_wayland_display_primary(void) {
return 0; // TODO
}
int kinc_wayland_count_displays(void) {
return wl_ctx.num_displays;
}
bool kinc_wayland_display_available(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS) {
return false;
}
struct kinc_wl_display *display = &wl_ctx.displays[display_index];
return display->output != NULL;
}
const char *kinc_wayland_display_name(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_wl_display *display = &wl_ctx.displays[display_index];
return display->name;
}
kinc_display_mode_t kinc_wayland_display_current_mode(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_wl_display *display = &wl_ctx.displays[display_index];
return display->modes[display->current_mode];
}
int kinc_wayland_display_count_available_modes(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_wl_display *display = &wl_ctx.displays[display_index];
return display->num_modes;
}
kinc_display_mode_t kinc_wayland_display_available_mode(int display_index, int mode_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_wl_display *display = &wl_ctx.displays[display_index];
if (mode_index >= display->num_modes)
mode_index = 0;
return display->modes[mode_index];
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
#ifndef KINC_WL_FN
#define KINC_WL_FN(ret, name, args)
#endif
KINC_WL_FUN(void, wl_event_queue_destroy, (struct wl_event_queue * queue))
#if KINC_WL_CHECK_VERSION(1, 20, 0)
KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_flags,
(struct wl_proxy * proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...))
KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_array_flags,
(struct wl_proxy * proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, union wl_argument *args))
#endif
KINC_WL_FUN(void, wl_proxy_marshal, (struct wl_proxy * p, uint32_t opcode, ...))
KINC_WL_FUN(void, wl_proxy_marshal_array, (struct wl_proxy * p, uint32_t opcode, union wl_argument *args))
KINC_WL_FUN(struct wl_proxy *, wl_proxy_create, (struct wl_proxy * factory, const struct wl_interface *interface))
KINC_WL_FUN(void *, wl_proxy_create_wrapper, (void *proxy))
KINC_WL_FUN(void, wl_proxy_wrapper_destroy, (void *proxy_wrapper))
KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_constructor, (struct wl_proxy * proxy, uint32_t opcode, const struct wl_interface *interface, ...))
KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_constructor_versioned,
(struct wl_proxy * proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, ...))
KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_array_constructor,
(struct wl_proxy * proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface))
KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_array_constructor_versioned,
(struct wl_proxy * proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface, uint32_t version))
KINC_WL_FUN(void, wl_proxy_destroy, (struct wl_proxy * proxy))
KINC_WL_FUN(int, wl_proxy_add_listener, (struct wl_proxy * proxy, void (**implementation)(void), void *data))
KINC_WL_FUN(const void *, wl_proxy_get_listener, (struct wl_proxy * proxy))
KINC_WL_FUN(int, wl_proxy_add_dispatcher, (struct wl_proxy * proxy, wl_dispatcher_func_t dispatcher_func, const void *dispatcher_data, void *data))
KINC_WL_FUN(void, wl_proxy_set_user_data, (struct wl_proxy * proxy, void *user_data))
KINC_WL_FUN(void *, wl_proxy_get_user_data, (struct wl_proxy * proxy))
KINC_WL_FUN(uint32_t, wl_proxy_get_version, (struct wl_proxy * proxy))
KINC_WL_FUN(uint32_t, wl_proxy_get_id, (struct wl_proxy * proxy))
KINC_WL_FUN(void, wl_proxy_set_tag, (struct wl_proxy * proxy, const char *const *tag))
KINC_WL_FUN(const char *const *, wl_proxy_get_tag, (struct wl_proxy * proxy))
KINC_WL_FUN(const char *, wl_proxy_get_class, (struct wl_proxy * proxy))
KINC_WL_FUN(void, wl_proxy_set_queue, (struct wl_proxy * proxy, struct wl_event_queue *queue))
KINC_WL_FUN(struct wl_display *, wl_display_connect, (const char *name))
KINC_WL_FUN(struct wl_display *, wl_display_connect_to_fd, (int fd))
KINC_WL_FUN(void, wl_display_disconnect, (struct wl_display * display))
KINC_WL_FUN(int, wl_display_get_fd, (struct wl_display * display))
KINC_WL_FUN(int, wl_display_dispatch, (struct wl_display * display))
KINC_WL_FUN(int, wl_display_dispatch_queue, (struct wl_display * display, struct wl_event_queue *queue))
KINC_WL_FUN(int, wl_display_dispatch_queue_pending, (struct wl_display * display, struct wl_event_queue *queue))
KINC_WL_FUN(int, wl_display_dispatch_pending, (struct wl_display * display))
KINC_WL_FUN(int, wl_display_get_error, (struct wl_display * display))
KINC_WL_FUN(uint32_t, wl_display_get_protocol_error, (struct wl_display * display, const struct wl_interface **interface, uint32_t *id))
KINC_WL_FUN(int, wl_display_flush, (struct wl_display * display))
KINC_WL_FUN(int, wl_display_roundtrip_queue, (struct wl_display * display, struct wl_event_queue *queue))
KINC_WL_FUN(int, wl_display_roundtrip, (struct wl_display * display))
KINC_WL_FUN(struct wl_event_queue *, wl_display_create_queue, (struct wl_display * display))
KINC_WL_FUN(int, wl_display_prepare_read_queue, (struct wl_display * display, struct wl_event_queue *queue))
KINC_WL_FUN(int, wl_display_prepare_read, (struct wl_display * display))
KINC_WL_FUN(void, wl_display_cancel_read, (struct wl_display * display))
KINC_WL_FUN(int, wl_display_read_events, (struct wl_display * display))
KINC_WL_FUN(void, wl_log_set_handler_client, (wl_log_func_t handler))

View File

@ -0,0 +1,367 @@
#pragma once
#ifndef _GNU_SOURCE
#define _GNU_SOURCE // put this here too, to make clangd happy
#endif
#include <kinc/display.h>
#include <kinc/log.h>
#include <kinc/system.h>
#include <wayland-client-core.h>
#include <wayland-cursor.h>
#define KINC_WL_CHECK_VERSION(x, y, z) \
(WAYLAND_VERSION_MAJOR > x || (WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR > y) || \
(WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR == y && WAYLAND_VERSION_MICRO >= z))
struct wl_surface;
struct kinc_wl_procs {
#define KINC_WL_FUN(ret, name, args) ret(*_##name) args;
#include "wayland-funs.h"
#undef KINC_WL_FUN
struct wl_cursor_theme *(*_wl_cursor_theme_load)(const char *name, int size, struct wl_shm *shm);
void (*_wl_cursor_theme_destroy)(struct wl_cursor_theme *theme);
struct wl_cursor *(*_wl_cursor_theme_get_cursor)(struct wl_cursor_theme *theme, const char *name);
struct wl_buffer *(*_wl_cursor_image_get_buffer)(struct wl_cursor_image *image);
int (*_wl_cursor_frame)(struct wl_cursor *cursor, uint32_t time);
int (*_wl_cursor_frame_and_duration)(struct wl_cursor *cursor, uint32_t time, uint32_t *duration);
#ifdef KINC_EGL
struct wl_egl_window *(*_wl_egl_window_create)(struct wl_surface *surface, int width, int height);
void (*_wl_egl_window_destroy)(struct wl_egl_window *egl_window);
void (*_wl_egl_window_resize)(struct wl_egl_window *egl_window, int width, int height, int dx, int dy);
void (*_wl_egl_window_get_attached_size)(struct wl_egl_window *egl_window, int *width, int *height);
#endif
};
#define wl_event_queue_destroy wl._wl_event_queue_destroy
#define wl_proxy_marshal_flags wl._wl_proxy_marshal_flags
#define wl_proxy_marshal_array_flags wl._wl_proxy_marshal_array_flags
#define wl_proxy_marshal wl._wl_proxy_marshal
#define wl_proxy_marshal_array wl._wl_proxy_marshal_array
#define wl_proxy_create wl._wl_proxy_create
#define wl_proxy_create_wrapper wl._wl_proxy_create_wrapper
#define wl_proxy_wrapper_destroy wl._wl_proxy_wrapper_destroy
#define wl_proxy_marshal_constructor wl._wl_proxy_marshal_constructor
#define wl_proxy_marshal_constructor_versioned wl._wl_proxy_marshal_constructor_versioned
#define wl_proxy_marshal_array_constructor wl._wl_proxy_marshal_array_constructor
#define wl_proxy_marshal_array_constructor_versioned wl._wl_proxy_marshal_array_constructor_versioned
#define wl_proxy_destroy wl._wl_proxy_destroy
#define wl_proxy_add_listener wl._wl_proxy_add_listener
#define wl_proxy_get_listener wl._wl_proxy_get_listener
#define wl_proxy_add_dispatcher wl._wl_proxy_add_dispatcher
#define wl_proxy_set_user_data wl._wl_proxy_set_user_data
#define wl_proxy_get_user_data wl._wl_proxy_get_user_data
#define wl_proxy_get_version wl._wl_proxy_get_version
#define wl_proxy_get_id wl._wl_proxy_get_id
#define wl_proxy_set_tag wl._wl_proxy_set_tag
#define wl_proxy_get_tag wl._wl_proxy_get_tag
#define wl_proxy_get_class wl._wl_proxy_get_class
#define wl_proxy_set_queue wl._wl_proxy_set_queue
#define wl_display_connect wl._wl_display_connect
#define wl_display_connect_to_fd wl._wl_display_connect_to_fd
#define wl_display_disconnect wl._wl_display_disconnect
#define wl_display_get_fd wl._wl_display_get_fd
#define wl_display_dispatch wl._wl_display_dispatch
#define wl_display_dispatch_queue wl._wl_display_dispatch_queue
#define wl_display_dispatch_queue_pending wl._wl_display_dispatch_queue_pending
#define wl_display_dispatch_pending wl._wl_display_dispatch_pending
#define wl_display_get_error wl._wl_display_get_error
#define wl_display_get_protocol_error wl._wl_display_get_protocol_error
#define wl_display_flush wl._wl_display_flush
#define wl_display_roundtrip_queue wl._wl_display_roundtrip_queue
#define wl_display_roundtrip wl._wl_display_roundtrip
#define wl_display_create_queue wl._wl_display_create_queue
#define wl_display_prepare_read_queue wl._wl_display_prepare_read_queue
#define wl_display_prepare_read wl._wl_display_prepare_read
#define wl_display_cancel_read wl._wl_display_cancel_read
#define wl_display_read_events wl._wl_display_read_events
#define wl_log_set_handler_client wl._wl_log_set_handler_client
#define wl_cursor_theme_load wl._wl_cursor_theme_load
#define wl_cursor_theme_destroy wl._wl_cursor_theme_destroy
#define wl_cursor_theme_get_cursor wl._wl_cursor_theme_get_cursor
#define wl_cursor_image_get_buffer wl._wl_cursor_image_get_buffer
#define wl_cursor_frame wl._wl_cursor_frame
#define wl_cursor_frame_and_duration wl._wl_cursor_frame_and_duration
#define wl_egl_window_create wl._wl_egl_window_create
#define wl_egl_window_destroy wl._wl_egl_window_destroy
#define wl_egl_window_resize wl._wl_egl_window_resize
extern struct kinc_wl_procs wl;
#include <wayland-client-protocol.h>
#include <wayland-generated/wayland-pointer-constraint.h>
#include <wayland-generated/wayland-relative-pointer.h>
#include <wayland-generated/wayland-tablet.h>
#include <wayland-generated/wayland-viewporter.h>
#include <wayland-generated/xdg-decoration.h>
#include <wayland-generated/xdg-shell.h>
#include <xkbcommon/xkbcommon.h>
struct kinc_xkb_procs {
struct xkb_context *(*xkb_context_new)(enum xkb_context_flags flags);
void (*xkb_context_unref)(struct xkb_context *context);
struct xkb_keymap *(*xkb_keymap_new_from_string)(struct xkb_context *context, const char *string, enum xkb_keymap_format format,
enum xkb_keymap_compile_flags flags);
struct xkb_state *(*xkb_state_new)(struct xkb_keymap *keymap);
xkb_keysym_t (*xkb_state_key_get_one_sym)(struct xkb_state *state, xkb_keycode_t key);
uint32_t (*xkb_state_key_get_utf32)(struct xkb_state *state, xkb_keycode_t key);
xkb_mod_mask_t (*xkb_state_serialize_mods)(struct xkb_state *state, enum xkb_state_component components);
enum xkb_state_component (*xkb_state_update_mask)(struct xkb_state *state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods,
xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout,
xkb_layout_index_t locked_layout);
int (*xkb_state_mod_name_is_active)(struct xkb_state *state, const char *name, enum xkb_state_component type);
int (*xkb_keymap_key_repeats)(struct xkb_keymap *keymap, xkb_keycode_t key);
};
extern struct kinc_xkb_procs wl_xkb;
#include <kinc/display.h>
#include <kinc/global.h>
#include <kinc/system.h>
#include <kinc/window.h>
#define MAXIMUM_WINDOWS 16
#define MAXIMUM_DISPLAYS 16
#define MAXIMUM_DISPLAY_MODES 16
struct kinc_wl_decoration {
struct wl_surface *surface;
struct wl_subsurface *subsurface;
struct wp_viewport *viewport;
};
enum kinc_wl_decoration_focus {
KINC_WL_DECORATION_FOCUS_MAIN,
KINC_WL_DECORATION_FOCUS_TOP,
KINC_WL_DECORATION_FOCUS_LEFT,
KINC_WL_DECORATION_FOCUS_RIGHT,
KINC_WL_DECORATION_FOCUS_BOTTOM,
KINC_WL_DECORATION_FOCUS_CLOSE_BUTTON,
KINC_WL_DECORATION_FOCUS_MAX_BUTTON,
KINC_WL_DECORATION_FOCUS_MIN_BUTTON
};
#define KINC_WL_DECORATION_WIDTH 10
#define KINC_WL_DECORATION_TOP_X 0
#define KINC_WL_DECORATION_TOP_Y -(KINC_WL_DECORATION_TOP_HEIGHT)
#define KINC_WL_DECORATION_TOP_WIDTH window->width
#define KINC_WL_DECORATION_TOP_HEIGHT KINC_WL_DECORATION_WIDTH * 3
#define KINC_WL_DECORATION_LEFT_X -10
#define KINC_WL_DECORATION_LEFT_Y -(KINC_WL_DECORATION_TOP_HEIGHT)
#define KINC_WL_DECORATION_LEFT_WIDTH KINC_WL_DECORATION_WIDTH
#define KINC_WL_DECORATION_LEFT_HEIGHT window->height + KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT
#define KINC_WL_DECORATION_RIGHT_X window->width
#define KINC_WL_DECORATION_RIGHT_Y -(KINC_WL_DECORATION_TOP_HEIGHT)
#define KINC_WL_DECORATION_RIGHT_WIDTH 10
#define KINC_WL_DECORATION_RIGHT_HEIGHT window->height + KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT
#define KINC_WL_DECORATION_BOTTOM_X 0
#define KINC_WL_DECORATION_BOTTOM_Y window->height
#define KINC_WL_DECORATION_BOTTOM_WIDTH window->width
#define KINC_WL_DECORATION_BOTTOM_HEIGHT KINC_WL_DECORATION_WIDTH
#define KINC_WL_DECORATION_CLOSE_X window->width - 10
#define KINC_WL_DECORATION_CLOSE_Y -20
#define KINC_WL_DECORATION_CLOSE_WIDTH 9
#define KINC_WL_DECORATION_CLOSE_HEIGHT 9
struct kinc_wl_window {
int display_index;
int window_id;
int width;
int height;
kinc_window_mode_t mode;
struct wl_surface *surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *toplevel;
struct zxdg_toplevel_decoration_v1 *xdg_decoration;
bool configured;
struct {
bool server_side;
enum kinc_wl_decoration_focus focus;
struct kinc_wl_decoration top;
struct kinc_wl_decoration left;
struct kinc_wl_decoration right;
struct kinc_wl_decoration bottom;
struct kinc_wl_decoration close;
struct kinc_wl_decoration max;
struct kinc_wl_decoration min;
struct wl_buffer *dec_buffer;
struct wl_buffer *close_buffer;
struct wl_buffer *max_buffer;
struct wl_buffer *min_buffer;
} decorations;
#ifdef KINC_EGL
struct wl_egl_window *egl_window;
#endif
};
struct kinc_wl_display {
struct wl_output *output;
int index;
int current_mode;
int num_modes;
char name[64];
int x;
int y;
int physical_width;
int physical_height;
int subpixel;
enum wl_output_transform transform;
enum wl_output_subpixel scale;
kinc_display_mode_t modes[MAXIMUM_DISPLAY_MODES];
};
struct kinc_wl_mouse {
struct kinc_wl_seat *seat;
int current_window;
int x;
int y;
int enter_serial;
const char *previous_cursor_name;
struct wl_pointer *pointer;
struct wl_surface *surface;
bool hidden;
bool locked;
struct zwp_locked_pointer_v1 *lock;
struct zwp_relative_pointer_v1 *relative;
int serial;
};
struct kinc_wl_keyboard {
struct kinc_wl_seat *seat;
struct wl_keyboard *keyboard;
struct xkb_keymap *keymap;
struct xkb_state *state;
bool ctrlDown;
int repeat_delay;
int repeat_rate;
int timerfd;
int last_key_code;
int last_character;
};
struct kinc_wl_data_offer {
struct wl_data_offer *id;
int mime_type_count;
char **mime_types;
uint32_t source_actions;
uint32_t dnd_action;
void (*callback)(void *data, size_t data_size, void *user_data);
void *user_data;
int read_fd;
void *buffer;
size_t buf_size;
size_t buf_pos;
struct kinc_wl_data_offer *next;
};
struct kinc_wl_data_source {
struct wl_data_source *source;
const char **mime_types;
int num_mime_types;
void *data;
size_t data_size;
};
struct kinc_wl_tablet_tool {
struct zwp_tablet_tool_v2 *id;
enum zwp_tablet_tool_v2_type type;
uint32_t capabilities;
uint64_t hardware_serial;
uint64_t hardware_id_wacom;
int current_window;
int x;
int y;
float current_pressure;
float current_distance;
void (*press)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/);
void (*move)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/);
void (*release)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/);
struct kinc_wl_tablet_seat *seat;
struct kinc_wl_tablet_tool *next;
};
struct kinc_wl_tablet {
struct zwp_tablet_v2 *id;
struct kinc_wl_tablet_seat *seat;
struct kinc_wl_tablet *next;
};
struct kinc_wl_tablet_seat {
struct zwp_tablet_seat_v2 *seat;
struct kinc_wl_tablet *tablets;
struct kinc_wl_tablet_tool *tablet_tools;
};
struct kinc_wl_seat {
struct wl_seat *seat;
struct kinc_wl_keyboard keyboard;
struct kinc_wl_mouse mouse;
struct wl_touch *touch;
struct kinc_wl_tablet_seat tablet_seat;
struct wl_data_device *data_device;
struct kinc_wl_data_offer *current_selection_offer;
struct kinc_wl_data_offer *current_dnd_offer;
int current_serial;
uint32_t capabilities;
char name[64];
};
struct wayland_context {
struct xkb_context *xkb_context;
struct wl_display *display;
struct wl_shm *shm;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_subcompositor *subcompositor;
struct wp_viewporter *viewporter;
struct kinc_wl_seat seat;
struct xdg_wm_base *xdg_wm_base;
struct zxdg_decoration_manager_v1 *decoration_manager;
struct wl_data_device_manager *data_device_manager;
struct zwp_tablet_manager_v2 *tablet_manager;
struct zwp_pointer_constraints_v1 *pointer_constraints;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
struct wl_cursor_theme *cursor_theme;
int cursor_size;
int num_windows;
struct kinc_wl_window windows[MAXIMUM_WINDOWS];
int num_displays;
struct kinc_wl_display displays[MAXIMUM_DISPLAYS];
struct kinc_wl_data_offer *data_offer_queue;
};
extern struct wayland_context wl_ctx;
struct kinc_wl_data_source *kinc_wl_create_data_source(struct kinc_wl_seat *seat, const char *mime_types[], int num_mime_types, void *data, size_t data_size);
void kinc_wl_data_source_destroy(struct kinc_wl_data_source *data_source);
void kinc_wl_data_offer_accept(struct kinc_wl_data_offer *offer, void (*callback)(void *data, size_t data_size, void *user_data), void *user_data);
void kinc_wl_destroy_data_offer(struct kinc_wl_data_offer *offer);
void kinc_wayland_set_selection(struct kinc_wl_seat *seat, const char *text, int serial);
void kinc_wayland_window_destroy(int window_index);

View File

@ -0,0 +1,469 @@
#include "wayland.h"
#include <kinc/image.h>
#include <kinc/window.h>
// for all that shared memory stuff later on
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#ifdef KINC_EGL
#define EGL_NO_PLATFORM_SPECIFIC_TYPES
#include <EGL/egl.h>
#endif
static void xdg_surface_handle_configure(void *data, struct xdg_surface *surface, uint32_t serial) {
xdg_surface_ack_configure(surface, serial);
struct kinc_wl_window *window = data;
window->configured = true;
}
void kinc_internal_resize(int, int, int);
void kinc_wayland_destroy_decoration(struct kinc_wl_decoration *);
void kinc_wayland_resize_decoration(struct kinc_wl_decoration *, int x, int y, int width, int height);
static void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) {
struct kinc_wl_window *window = data;
if ((width <= 0 || height <= 0) || (width == window->width + (KINC_WL_DECORATION_WIDTH * 2) &&
height == window->height + KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT)) {
return;
}
if (window->decorations.server_side) {
window->width = width;
window->height = height;
}
else {
window->width = width - (KINC_WL_DECORATION_WIDTH * 2);
window->height = height - KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT;
}
enum xdg_toplevel_state *state;
wl_array_for_each(state, states) {
switch (*state) {
case XDG_TOPLEVEL_STATE_ACTIVATED:
kinc_internal_foreground_callback();
break;
case XDG_TOPLEVEL_STATE_RESIZING:
break;
case XDG_TOPLEVEL_STATE_MAXIMIZED:
break;
default:
break;
}
}
kinc_internal_resize(window->window_id, window->width, window->height);
kinc_internal_call_resize_callback(window->window_id, window->width, window->height);
if (window->decorations.server_side) {
xdg_surface_set_window_geometry(window->xdg_surface, 0, 0, window->width, window->height);
}
else {
xdg_surface_set_window_geometry(window->xdg_surface, KINC_WL_DECORATION_LEFT_X, KINC_WL_DECORATION_TOP_Y,
window->width + (KINC_WL_DECORATION_WIDTH * 2),
window->height + KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT);
}
#ifdef KINC_EGL
wl_egl_window_resize(window->egl_window, window->width, window->height, 0, 0);
#endif
kinc_wayland_resize_decoration(&window->decorations.top, KINC_WL_DECORATION_TOP_X, KINC_WL_DECORATION_TOP_Y, KINC_WL_DECORATION_TOP_WIDTH,
KINC_WL_DECORATION_TOP_HEIGHT);
kinc_wayland_resize_decoration(&window->decorations.left, KINC_WL_DECORATION_LEFT_X, KINC_WL_DECORATION_LEFT_Y, KINC_WL_DECORATION_LEFT_WIDTH,
KINC_WL_DECORATION_LEFT_HEIGHT);
kinc_wayland_resize_decoration(&window->decorations.right, KINC_WL_DECORATION_RIGHT_X, KINC_WL_DECORATION_RIGHT_Y, KINC_WL_DECORATION_RIGHT_WIDTH,
KINC_WL_DECORATION_RIGHT_HEIGHT);
kinc_wayland_resize_decoration(&window->decorations.bottom, KINC_WL_DECORATION_BOTTOM_X, KINC_WL_DECORATION_BOTTOM_Y, KINC_WL_DECORATION_BOTTOM_WIDTH,
KINC_WL_DECORATION_BOTTOM_HEIGHT);
kinc_wayland_resize_decoration(&window->decorations.close, KINC_WL_DECORATION_CLOSE_X, KINC_WL_DECORATION_CLOSE_Y, KINC_WL_DECORATION_CLOSE_WIDTH,
KINC_WL_DECORATION_CLOSE_HEIGHT);
}
void kinc_wayland_window_destroy(int window_index);
static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) {
struct kinc_wl_window *window = data;
if (kinc_internal_call_close_callback(window->window_id)) {
kinc_window_destroy(window->window_id);
if (wl_ctx.num_windows <= 0) {
// no windows left, stop
kinc_stop();
}
}
}
static int create_shm_fd(off_t size) {
int fd = -1;
#if defined(__linux__)
#if defined(__GLIBC__) && !__GLIBC_PREREQ(2, 27)
#else
// memfd_create is available since glibc 2.27 and musl 1.1.20
// the syscall is available since linux 3.17
// at the time of writing (04/02/2022) these requirements are fullfilled for the "LTS" versions of the following distributions
// Ubuntu 18.04
// Debian Stretch
// Alpine 3.12
// Fedora 34
fd = memfd_create("kinc-wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd >= 0) {
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
int ret = posix_fallocate(fd, 0, size);
if (ret != 0) {
close(fd);
errno = ret;
return -1;
}
}
else // fall back to a temp file
#endif
#endif
{
static const char template[] = "/kinc-shared-XXXXXX";
const char *path = getenv("XDG_RUNTIME_DIR");
if (!path) {
errno = ENOENT;
return -1;
}
char *name = malloc(strlen(path) + sizeof(template));
strcpy(name, path);
strcat(name, template);
fd = mkostemp(name, O_CLOEXEC);
if (fd >= 0)
unlink(name);
free(name);
if (fd < 0)
return -1;
int ret = ftruncate(fd, size);
if (ret != 0) {
close(fd);
errno = ret;
return -1;
}
}
return fd;
}
struct wl_buffer *kinc_wayland_create_shm_buffer(const kinc_image_t *image) {
int stride = image->width * 4;
int length = image->width * image->height * 4;
const int fd = create_shm_fd(length);
if (fd < 0) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland: Creating a buffer file for %d B failed: %s", length, strerror(errno));
return NULL;
}
void *data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland: mmap failed: %s", strerror(errno));
close(fd);
return NULL;
}
struct wl_shm_pool *pool = wl_shm_create_pool(wl_ctx.shm, fd, length);
close(fd);
memcpy(data, image->data, image->width * image->height * 4);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, image->width, image->height, stride, WL_SHM_FORMAT_ARGB8888);
munmap(data, length);
wl_shm_pool_destroy(pool);
return buffer;
}
static int grey_data[] = {0xFF333333};
static int close_data[] = {
0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000,
0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF,
};
// image format is argb32, but kinc does not have that, so let's lie to it
static kinc_image_t grey_image = {
1, 1, 0, KINC_IMAGE_FORMAT_RGBA32, 0, KINC_IMAGE_COMPRESSION_NONE, grey_data, sizeof(grey_data),
};
static kinc_image_t close_image = {
9, 9, 0, KINC_IMAGE_FORMAT_RGBA32, 0, KINC_IMAGE_COMPRESSION_NONE, close_data, sizeof(close_data),
};
void kinc_wayland_create_decoration(struct kinc_wl_decoration *decoration, struct wl_surface *parent, struct wl_buffer *buffer, bool opaque, int x, int y,
int width, int height) {
decoration->surface = wl_compositor_create_surface(wl_ctx.compositor);
decoration->subsurface = wl_subcompositor_get_subsurface(wl_ctx.subcompositor, decoration->surface, parent);
wl_subsurface_set_position(decoration->subsurface, x, y);
decoration->viewport = wp_viewporter_get_viewport(wl_ctx.viewporter, decoration->surface);
wp_viewport_set_destination(decoration->viewport, width, height);
if (buffer)
wl_surface_attach(decoration->surface, buffer, 0, 0);
if (opaque) {
struct wl_region *region = wl_compositor_create_region(wl_ctx.compositor);
wl_region_add(region, 0, 0, width, height);
wl_surface_set_opaque_region(decoration->surface, region);
wl_surface_commit(decoration->surface);
wl_region_destroy(region);
}
else
wl_surface_commit(decoration->surface);
}
void kinc_wayland_resize_decoration(struct kinc_wl_decoration *decoration, int x, int y, int width, int height) {
if (decoration->surface) {
wl_subsurface_set_position(decoration->subsurface, x, y);
wp_viewport_set_destination(decoration->viewport, width, height);
wl_surface_commit(decoration->surface);
}
}
void kinc_wayland_destroy_decoration(struct kinc_wl_decoration *decoration) {
if (decoration->subsurface)
wl_subsurface_destroy(decoration->subsurface);
if (decoration->surface)
wl_surface_destroy(decoration->surface);
if (decoration->viewport)
wp_viewport_destroy(decoration->viewport);
decoration->surface = NULL;
decoration->subsurface = NULL;
decoration->viewport = NULL;
}
void kinc_wayland_destroy_decorations(struct kinc_wl_window *window) {
kinc_wayland_destroy_decoration(&window->decorations.top);
kinc_wayland_destroy_decoration(&window->decorations.left);
kinc_wayland_destroy_decoration(&window->decorations.right);
kinc_wayland_destroy_decoration(&window->decorations.bottom);
kinc_wayland_destroy_decoration(&window->decorations.close);
}
void kinc_wayland_create_decorations(struct kinc_wl_window *window) {
if (!window->decorations.dec_buffer) {
window->decorations.dec_buffer = kinc_wayland_create_shm_buffer(&grey_image);
window->decorations.close_buffer = kinc_wayland_create_shm_buffer(&close_image);
window->decorations.max_buffer = kinc_wayland_create_shm_buffer(&grey_image);
window->decorations.min_buffer = kinc_wayland_create_shm_buffer(&grey_image);
}
kinc_wayland_create_decoration(&window->decorations.top, window->surface, window->decorations.dec_buffer, true, KINC_WL_DECORATION_TOP_X,
KINC_WL_DECORATION_TOP_Y, KINC_WL_DECORATION_TOP_WIDTH, KINC_WL_DECORATION_TOP_HEIGHT);
kinc_wayland_create_decoration(&window->decorations.left, window->surface, window->decorations.dec_buffer, true, KINC_WL_DECORATION_LEFT_X,
KINC_WL_DECORATION_LEFT_Y, KINC_WL_DECORATION_LEFT_WIDTH, KINC_WL_DECORATION_LEFT_HEIGHT);
kinc_wayland_create_decoration(&window->decorations.right, window->surface, window->decorations.dec_buffer, true, KINC_WL_DECORATION_RIGHT_X,
KINC_WL_DECORATION_RIGHT_Y, KINC_WL_DECORATION_RIGHT_WIDTH, KINC_WL_DECORATION_RIGHT_HEIGHT);
kinc_wayland_create_decoration(&window->decorations.bottom, window->surface, window->decorations.dec_buffer, true, KINC_WL_DECORATION_BOTTOM_X,
KINC_WL_DECORATION_BOTTOM_Y, KINC_WL_DECORATION_BOTTOM_WIDTH, KINC_WL_DECORATION_BOTTOM_HEIGHT);
kinc_wayland_create_decoration(&window->decorations.close, window->surface, window->decorations.close_buffer, true, KINC_WL_DECORATION_CLOSE_X,
KINC_WL_DECORATION_CLOSE_Y, KINC_WL_DECORATION_CLOSE_WIDTH, KINC_WL_DECORATION_CLOSE_HEIGHT);
}
void xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode) {
struct kinc_wl_window *window = data;
if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
window->decorations.server_side = false;
if (window->decorations.top.surface) {
kinc_wayland_destroy_decorations(window);
}
if (window->mode == KINC_WINDOW_MODE_WINDOW) {
kinc_wayland_create_decorations(window);
}
}
else if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) {
window->decorations.server_side = true;
if (window->decorations.top.surface) {
kinc_wayland_destroy_decorations(window);
}
}
}
void wl_surface_handle_enter(void *data, struct wl_surface *wl_surface, struct wl_output *output) {
struct kinc_wl_window *window = wl_surface_get_user_data(wl_surface);
struct kinc_wl_display *display = wl_output_get_user_data(output);
if (display && window) {
window->display_index = display->index;
}
}
void wl_surface_handle_leave(void *data, struct wl_surface *wl_surface, struct wl_output *output) {}
static const struct wl_surface_listener wl_surface_listener = {
wl_surface_handle_enter,
wl_surface_handle_leave,
};
static const struct xdg_surface_listener xdg_surface_listener = {
xdg_surface_handle_configure,
};
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
xdg_toplevel_handle_configure,
xdg_toplevel_handle_close,
};
static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = {
xdg_toplevel_decoration_configure,
};
void kinc_wayland_window_set_title(int window_index, const char *title);
void kinc_wayland_window_change_mode(int window_index, kinc_window_mode_t mode);
int kinc_wayland_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) {
int window_index = -1;
for (int i = 0; i < MAXIMUM_WINDOWS; i++) {
if (wl_ctx.windows[i].surface == NULL) {
window_index = i;
break;
}
}
if (window_index == -1) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Too much windows (maximum is %i)", MAXIMUM_WINDOWS);
exit(1);
}
struct kinc_wl_window *window = &wl_ctx.windows[window_index];
window->window_id = window_index;
window->width = win->width;
window->height = win->height;
window->mode = KINC_WINDOW_MODE_WINDOW;
window->surface = wl_compositor_create_surface(wl_ctx.compositor);
wl_surface_set_user_data(window->surface, window);
wl_surface_add_listener(window->surface, &wl_surface_listener, NULL);
window->xdg_surface = xdg_wm_base_get_xdg_surface(wl_ctx.xdg_wm_base, window->surface);
xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
window->toplevel = xdg_surface_get_toplevel(window->xdg_surface);
xdg_toplevel_add_listener(window->toplevel, &xdg_toplevel_listener, window);
kinc_wayland_window_set_title(window_index, win->title);
if (wl_ctx.decoration_manager) {
window->xdg_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(wl_ctx.decoration_manager, window->toplevel);
#ifdef KINC_WAYLAND_FORCE_CSD
zxdg_toplevel_decoration_v1_set_mode(window->xdg_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
#endif
zxdg_toplevel_decoration_v1_add_listener(window->xdg_decoration, &xdg_toplevel_decoration_listener, window);
}
else {
window->decorations.server_side = false;
kinc_wayland_create_decorations(window);
}
#ifdef KINC_EGL
window->egl_window = wl_egl_window_create(window->surface, window->width, window->height);
#endif
wl_surface_commit(window->surface);
kinc_wayland_window_change_mode(window_index, win->mode);
wl_ctx.num_windows++;
while (!window->configured) {
wl_display_roundtrip(wl_ctx.display);
}
return window_index;
}
void kinc_wayland_window_destroy(int window_index) {
struct kinc_wl_window *window = &wl_ctx.windows[window_index];
#ifdef KINC_EGL
wl_egl_window_destroy(window->egl_window);
#endif
if (window->xdg_decoration) {
zxdg_toplevel_decoration_v1_destroy(window->xdg_decoration);
}
xdg_toplevel_destroy(window->toplevel);
xdg_surface_destroy(window->xdg_surface);
wl_surface_destroy(window->surface);
*window = (struct kinc_wl_window){0};
wl_ctx.num_windows--;
}
void kinc_wayland_window_set_title(int window_index, const char *title) {
struct kinc_wl_window *window = &wl_ctx.windows[window_index];
xdg_toplevel_set_title(window->toplevel, title == NULL ? "" : title);
}
int kinc_wayland_window_x(int window_index) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support getting the window position.");
return 0;
}
int kinc_wayland_window_y(int window_index) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support getting the window position.");
return 0;
}
void kinc_wayland_window_move(int window_index, int x, int y) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support setting the window position.");
}
int kinc_wayland_window_width(int window_index) {
return wl_ctx.windows[window_index].width;
}
int kinc_wayland_window_height(int window_index) {
return wl_ctx.windows[window_index].height;
}
void kinc_wayland_window_resize(int window_index, int width, int height) {
kinc_log(KINC_LOG_LEVEL_WARNING, "TODO: resizing windows");
}
void kinc_wayland_window_show(int window_index) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support unhiding windows.");
}
void kinc_wayland_window_hide(int window_index) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support hiding windows.");
}
kinc_window_mode_t kinc_wayland_window_get_mode(int window_index) {
return wl_ctx.windows[window_index].mode;
}
void kinc_wayland_window_change_mode(int window_index, kinc_window_mode_t mode) {
struct kinc_wl_window *window = &wl_ctx.windows[window_index];
if (mode == window->mode) {
return;
}
switch (mode) {
case KINC_WINDOW_MODE_WINDOW:
if (window->mode == KINC_WINDOW_MODE_FULLSCREEN || window->mode == KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
window->mode = KINC_WINDOW_MODE_WINDOW;
xdg_toplevel_unset_fullscreen(window->toplevel);
}
break;
case KINC_WINDOW_MODE_FULLSCREEN:
case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
if (window->mode == KINC_WINDOW_MODE_WINDOW) {
window->mode = mode;
struct kinc_wl_display *display = &wl_ctx.displays[window->display_index];
xdg_toplevel_set_fullscreen(window->toplevel, display->output);
}
break;
}
}
int kinc_wayland_window_display(int window_index) {
struct kinc_wl_window *window = &wl_ctx.windows[window_index];
return window->display_index;
}
int kinc_wayland_count_windows() {
return wl_ctx.num_windows;
}

View File

@ -0,0 +1,140 @@
#include "funcs.h"
#include <kinc/display.h>
#include <kinc/graphics4/graphics.h>
#include <kinc/window.h>
#include <string.h>
#ifdef KINC_EGL
EGLDisplay kinc_egl_get_display() {
return procs.egl_get_display();
}
EGLNativeWindowType kinc_egl_get_native_window(EGLDisplay display, EGLConfig config, int window_index) {
return procs.egl_get_native_window(display, config, window_index);
}
#endif
#ifdef KINC_VULKAN
void kinc_vulkan_get_instance_extensions(const char **extensions, int *count, int max) {
procs.vulkan_get_instance_extensions(extensions, count, max);
}
VkBool32 kinc_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) {
return procs.vulkan_get_physical_device_presentation_support(physicalDevice, queueFamilyIndex);
}
VkResult kinc_vulkan_create_surface(VkInstance instance, int window_index, VkSurfaceKHR *surface) {
return procs.vulkan_create_surface(instance, window_index, surface);
}
#endif
int kinc_count_windows(void) {
return procs.count_windows();
}
int kinc_window_x(int window_index) {
return procs.window_x(window_index);
}
int kinc_window_y(int window_index) {
return procs.window_y(window_index);
}
int kinc_window_width(int window_index) {
return procs.window_width(window_index);
}
int kinc_window_height(int window_index) {
return procs.window_height(window_index);
}
void kinc_window_resize(int window_index, int width, int height) {
procs.window_resize(window_index, width, height);
}
void kinc_window_move(int window_index, int x, int y) {
procs.window_move(window_index, x, y);
}
void kinc_window_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) {}
void kinc_window_change_features(int window_index, int features) {}
void kinc_window_change_mode(int window_index, kinc_window_mode_t mode) {
procs.window_change_mode(window_index, mode);
}
int kinc_window_display(int window_index) {
return procs.window_display(window_index);
}
void kinc_window_destroy(int window_index) {
kinc_g4_internal_destroy_window(window_index);
procs.window_destroy(window_index);
}
void kinc_window_show(int window_index) {
procs.window_show(window_index);
}
void kinc_window_hide(int window_index) {
procs.window_hide(window_index);
}
void kinc_window_set_title(int window_index, const char *title) {
procs.window_set_title(window_index, title);
}
int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) {
int index = procs.window_create(win, frame);
kinc_g4_internal_init_window(index, frame->depth_bits, frame->stencil_bits, frame->vertical_sync);
return index;
}
static struct {
void (*resize_callback)(int width, int height, void *data);
void *resize_data;
void (*ppi_callback)(int ppi, void *data);
void *ppi_data;
bool (*close_callback)(void *data);
void *close_data;
} kinc_internal_window_callbacks[16];
void kinc_window_set_resize_callback(int window_index, void (*callback)(int width, int height, void *data), void *data) {
kinc_internal_window_callbacks[window_index].resize_callback = callback;
kinc_internal_window_callbacks[window_index].resize_data = data;
}
void kinc_internal_call_resize_callback(int window_index, int width, int height) {
if (kinc_internal_window_callbacks[window_index].resize_callback != NULL) {
kinc_internal_window_callbacks[window_index].resize_callback(width, height, kinc_internal_window_callbacks[window_index].resize_data);
}
}
void kinc_window_set_ppi_changed_callback(int window_index, void (*callback)(int ppi, void *data), void *data) {
kinc_internal_window_callbacks[window_index].ppi_callback = callback;
kinc_internal_window_callbacks[window_index].ppi_data = data;
}
void kinc_internal_call_ppi_changed_callback(int window_index, int ppi) {
if (kinc_internal_window_callbacks[window_index].ppi_callback != NULL) {
kinc_internal_window_callbacks[window_index].ppi_callback(ppi, kinc_internal_window_callbacks[window_index].resize_data);
}
}
void kinc_window_set_close_callback(int window_index, bool (*callback)(void *data), void *data) {
kinc_internal_window_callbacks[window_index].close_callback = callback;
kinc_internal_window_callbacks[window_index].close_data = data;
}
bool kinc_internal_call_close_callback(int window_index) {
if (kinc_internal_window_callbacks[window_index].close_callback != NULL) {
return kinc_internal_window_callbacks[window_index].close_callback(kinc_internal_window_callbacks[window_index].close_data);
}
else {
return true;
}
}
kinc_window_mode_t kinc_window_get_mode(int window_index) {
return procs.window_get_mode(window_index);
}

View File

@ -0,0 +1,221 @@
#include "x11.h"
#include <string.h>
// TODO: deal with monitor hotplugging and such
void kinc_x11_display_init(void) {
int eventBase;
int errorBase;
bool hasXinerama = (xlib.XineramaQueryExtension(x11_ctx.display, &eventBase, &errorBase) && xlib.XineramaIsActive(x11_ctx.display));
XineramaScreenInfo *xinerama_screens = NULL;
int xinerama_screen_count = 0;
if (hasXinerama) {
xinerama_screens = xlib.XineramaQueryScreens(x11_ctx.display, &xinerama_screen_count);
}
Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display));
XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
RROutput primary_output = xlib.XRRGetOutputPrimary(x11_ctx.display, root_window);
for (int i = 0; i < screen_resources->noutput; i++) {
if (i >= MAXIMUM_DISPLAYS) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Too many screens (maximum %i)", MAXIMUM_DISPLAYS);
break;
}
XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[i]);
if (output_info->connection != RR_Connected || output_info->crtc == None) {
xlib.XRRFreeOutputInfo(output_info);
continue;
}
XRRCrtcInfo *crtc_info = xlib.XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc);
struct kinc_x11_display *display = &x11_ctx.displays[x11_ctx.num_displays++];
display->index = i;
strncpy(display->name, output_info->name, sizeof(display->name));
display->x = crtc_info->x;
display->y = crtc_info->y;
display->width = crtc_info->width;
display->height = crtc_info->height;
display->primary = screen_resources->outputs[i] == primary_output;
display->crtc = output_info->crtc;
display->output = screen_resources->outputs[i];
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeCrtcInfo(crtc_info);
}
xlib.XRRFreeScreenResources(screen_resources);
if (hasXinerama) {
xlib.XFree(xinerama_screens);
}
}
int kinc_x11_display_primary(void) {
for (int i = 0; i < x11_ctx.num_displays; i++) {
if (x11_ctx.displays[i].primary) {
return i;
}
}
return 0;
}
int kinc_x11_count_displays(void) {
return x11_ctx.num_displays;
}
bool kinc_x11_display_available(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS) {
return false;
}
return x11_ctx.displays[display_index].output != None;
}
const char *kinc_x11_display_name(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS) {
return "";
}
return x11_ctx.displays[display_index].name;
}
kinc_display_mode_t kinc_x11_display_current_mode(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_x11_display *display = &x11_ctx.displays[display_index];
kinc_display_mode_t mode;
mode.x = 0;
mode.y = 0;
mode.width = display->width;
mode.height = display->height;
mode.frequency = 60;
mode.bits_per_pixel = 32;
mode.pixels_per_inch = 96;
Window root_window = DefaultRootWindow(x11_ctx.display);
XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]);
if (output_info->connection != RR_Connected || output_info->crtc == None) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index);
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return mode;
}
XRRCrtcInfo *crtc_info = xlib.XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc);
for (int j = 0; j < output_info->nmode; j++) {
RRMode rr_mode = crtc_info->mode;
XRRModeInfo *mode_info = NULL;
for (int k = 0; k < screen_resources->nmode; k++) {
if (screen_resources->modes[k].id == rr_mode) {
mode_info = &screen_resources->modes[k];
break;
}
}
if (mode_info == NULL) {
continue;
}
mode.x = display->x;
mode.y = display->y;
mode.width = mode_info->width;
mode.height = mode_info->height;
mode.pixels_per_inch = 96;
mode.bits_per_pixel = 32;
if (mode_info->hTotal && mode_info->vTotal) {
mode.frequency = (mode_info->dotClock / (mode_info->hTotal * mode_info->vTotal));
}
else {
mode.frequency = 60;
}
}
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeCrtcInfo(crtc_info);
xlib.XRRFreeScreenResources(screen_resources);
return mode;
}
int kinc_x11_display_count_available_modes(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_x11_display *display = &x11_ctx.displays[display_index];
Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display));
XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]);
if (output_info->connection != RR_Connected || output_info->crtc == None) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index);
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return 0;
}
int num_modes = output_info->nmode;
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return num_modes;
}
kinc_display_mode_t kinc_x11_display_available_mode(int display_index, int mode_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_x11_display *display = &x11_ctx.displays[display_index];
kinc_display_mode_t mode;
mode.x = 0;
mode.y = 0;
mode.width = display->width;
mode.height = display->height;
mode.frequency = 60;
mode.bits_per_pixel = 32;
mode.pixels_per_inch = 96;
Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display));
XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]);
if (output_info->connection != RR_Connected || output_info->crtc == None) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index);
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return mode;
}
if (mode_index >= output_info->nmode) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Invalid mode index %i.", mode_index);
}
RRMode rr_mode = output_info->modes[mode_index];
XRRModeInfo *mode_info = NULL;
for (int k = 0; k < screen_resources->nmode; k++) {
if (screen_resources->modes[k].id == rr_mode) {
mode_info = &screen_resources->modes[k];
break;
}
}
if (mode_info != NULL) {
mode.x = display->x;
mode.y = display->y;
mode.width = mode_info->width;
mode.height = mode_info->height;
mode.pixels_per_inch = 96;
mode.bits_per_pixel = 32;
if (mode_info->hTotal && mode_info->vTotal) {
mode.frequency = (mode_info->dotClock / (mode_info->hTotal * mode_info->vTotal));
}
else {
mode.frequency = 60;
}
}
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return mode;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,206 @@
#include "x11.h"
#include <stdlib.h>
struct MwmHints {
// These correspond to XmRInt resources. (VendorSE.c)
int flags;
int functions;
int decorations;
int input_mode;
int status;
};
#define MWM_HINTS_DECORATIONS (1L << 1)
void kinc_x11_window_set_title(int window_index, const char *title);
void kinc_x11_window_change_mode(int window_index, kinc_window_mode_t mode);
int kinc_x11_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) {
int window_index = -1;
for (int i = 0; i < MAXIMUM_WINDOWS; i++) {
if (x11_ctx.windows[i].window == None) {
window_index = i;
break;
}
}
if (window_index == -1) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Too much windows (maximum is %i)", MAXIMUM_WINDOWS);
exit(1);
}
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
window->window_index = window_index;
window->width = win->width;
window->height = win->height;
Visual *visual = NULL;
XSetWindowAttributes set_window_attribs = {0};
set_window_attribs.border_pixel = 0;
set_window_attribs.event_mask =
KeyPressMask | KeyReleaseMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask;
int screen = DefaultScreen(x11_ctx.display);
visual = DefaultVisual(x11_ctx.display, screen);
int depth = DefaultDepth(x11_ctx.display, screen);
set_window_attribs.colormap = xlib.XCreateColormap(x11_ctx.display, RootWindow(x11_ctx.display, screen), visual, AllocNone);
window->window = xlib.XCreateWindow(x11_ctx.display, RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display)), 0, 0, win->width, win->height, 0, depth,
InputOutput, visual, CWBorderPixel | CWColormap | CWEventMask, &set_window_attribs);
static char nameClass[256];
static const char *nameClassAddendum = "_KincApplication";
strncpy(nameClass, kinc_application_name(), sizeof(nameClass) - strlen(nameClassAddendum) - 1);
strcat(nameClass, nameClassAddendum);
char resNameBuffer[256];
strncpy(resNameBuffer, kinc_application_name(), 256);
XClassHint classHint = {.res_name = resNameBuffer, .res_class = nameClass};
xlib.XSetClassHint(x11_ctx.display, window->window, &classHint);
xlib.XSetLocaleModifiers("@im=none");
window->xInputMethod = xlib.XOpenIM(x11_ctx.display, NULL, NULL, NULL);
window->xInputContext = xlib.XCreateIC(window->xInputMethod, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window->window, NULL);
xlib.XSetICFocus(window->xInputContext);
window->mode = KINC_WINDOW_MODE_WINDOW;
kinc_x11_window_change_mode(window_index, win->mode);
xlib.XMapWindow(x11_ctx.display, window->window);
Atom XdndVersion = 5;
xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&XdndVersion, 1);
xlib.XSetWMProtocols(x11_ctx.display, window->window, &x11_ctx.atoms.WM_DELETE_WINDOW, 1);
kinc_x11_window_set_title(window_index, win->title);
if (x11_ctx.pen.id != -1) {
xlib.XSelectExtensionEvent(x11_ctx.display, window->window, &x11_ctx.pen.motionClass, 1);
}
if (x11_ctx.eraser.id != -1) {
xlib.XSelectExtensionEvent(x11_ctx.display, window->window, &x11_ctx.eraser.motionClass, 1);
}
x11_ctx.num_windows++;
return window_index;
}
void kinc_x11_window_destroy(int window_index) {
xlib.XFlush(x11_ctx.display);
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
xlib.XDestroyIC(window->xInputContext);
xlib.XCloseIM(window->xInputMethod);
xlib.XDestroyWindow(x11_ctx.display, window->window);
xlib.XFlush(x11_ctx.display);
*window = (struct kinc_x11_window){0};
x11_ctx.num_windows--;
}
void kinc_x11_window_set_title(int window_index, const char *_title) {
const char *title = _title == NULL ? "" : _title;
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.NET_WM_NAME, x11_ctx.atoms.UTF8_STRING, 8, PropModeReplace, (unsigned char *)title,
strlen(title));
xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.NET_WM_ICON_NAME, x11_ctx.atoms.UTF8_STRING, 8, PropModeReplace, (unsigned char *)title,
strlen(title));
xlib.XFlush(x11_ctx.display);
}
int kinc_x11_window_x(int window_index) {
kinc_log(KINC_LOG_LEVEL_ERROR, "x11 does not support getting the window position.");
return 0;
}
int kinc_x11_window_y(int window_index) {
kinc_log(KINC_LOG_LEVEL_ERROR, "x11 does not support getting the window position.");
return 0;
}
void kinc_x11_window_move(int window_index, int x, int y) {
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
xlib.XMoveWindow(x11_ctx.display, window->window, x, y);
}
int kinc_x11_window_width(int window_index) {
return x11_ctx.windows[window_index].width;
}
int kinc_x11_window_height(int window_index) {
return x11_ctx.windows[window_index].height;
}
void kinc_x11_window_resize(int window_index, int width, int height) {
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
xlib.XResizeWindow(x11_ctx.display, window->window, width, height);
}
void kinc_x11_window_show(int window_index) {
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
xlib.XMapWindow(x11_ctx.display, window->window);
}
void kinc_x11_window_hide(int window_index) {
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
xlib.XUnmapWindow(x11_ctx.display, window->window);
}
kinc_window_mode_t kinc_x11_window_get_mode(int window_index) {
return x11_ctx.windows[window_index].mode;
}
void kinc_x11_window_change_mode(int window_index, kinc_window_mode_t mode) {
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
if (mode == window->mode) {
return;
}
bool fullscreen = false;
switch (mode) {
case KINC_WINDOW_MODE_WINDOW:
if (window->mode == KINC_WINDOW_MODE_FULLSCREEN) {
window->mode = KINC_WINDOW_MODE_WINDOW;
fullscreen = false;
}
else {
return;
}
break;
case KINC_WINDOW_MODE_FULLSCREEN:
case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
if (window->mode == KINC_WINDOW_MODE_WINDOW) {
window->mode = KINC_WINDOW_MODE_FULLSCREEN;
fullscreen = true;
}
else {
return;
}
break;
}
XEvent xev;
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
xev.xclient.window = window->window;
xev.xclient.message_type = x11_ctx.atoms.NET_WM_STATE;
xev.xclient.format = 32;
xev.xclient.data.l[0] = fullscreen ? 1 : 0;
xev.xclient.data.l[1] = x11_ctx.atoms.NET_WM_STATE_FULLSCREEN;
xev.xclient.data.l[2] = 0;
xlib.XMapWindow(x11_ctx.display, window->window);
xlib.XSendEvent(x11_ctx.display, DefaultRootWindow(x11_ctx.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
xlib.XFlush(x11_ctx.display);
}
int kinc_x11_window_display(int window_index) {
struct kinc_x11_window *window = &x11_ctx.windows[window_index];
return window->display_index;
}
int kinc_x11_count_windows() {
return x11_ctx.num_windows;
}

View File

@ -0,0 +1,200 @@
#pragma once
#include <kinc/display.h>
#include <kinc/log.h>
#include <kinc/system.h>
#include <kinc/window.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/Xinerama.h>
#include <X11/extensions/Xrandr.h>
#ifdef KINC_EGL
#define EGL_NO_PLATFORM_SPECIFIC_TYPES
#include <EGL/egl.h>
#endif
#define MAXIMUM_WINDOWS 16
#define MAXIMUM_DISPLAYS 16
struct kinc_x11_window {
int display_index;
int window_index;
int width;
int height;
kinc_window_mode_t mode;
Window window;
XIM xInputMethod;
XIC xInputContext;
};
struct kinc_x11_display {
int index;
int current_mode;
int num_modes;
int x;
int y;
int width;
int height;
bool primary;
char name[64];
RROutput output;
RRCrtc crtc;
};
struct kinc_x11_mouse {
int current_window;
int x;
int y;
};
struct kinc_x11_atoms {
Atom XdndAware;
Atom XdndDrop;
Atom XdndEnter;
Atom XdndTextUriList;
Atom XdndStatus;
Atom XdndActionCopy;
Atom XdndSelection;
Atom CLIPBOARD;
Atom UTF8_STRING;
Atom XSEL_DATA;
Atom TARGETS;
Atom MULTIPLE;
Atom TEXT_PLAIN;
Atom WM_DELETE_WINDOW;
Atom MOTIF_WM_HINTS;
Atom NET_WM_NAME;
Atom NET_WM_ICON_NAME;
Atom NET_WM_STATE;
Atom NET_WM_STATE_FULLSCREEN;
Atom MOUSE;
Atom TABLET;
Atom KEYBOARD;
Atom TOUCHSCREEN;
Atom TOUCHPAD;
Atom BUTTONBOX;
Atom BARCODE;
Atom TRACKBALL;
Atom QUADRATURE;
Atom ID_MODULE;
Atom ONE_KNOB;
Atom NINE_KNOB;
Atom KNOB_BOX;
Atom SPACEBALL;
Atom DATAGLOVE;
Atom EYETRACKER;
Atom CURSORKEYS;
Atom FOOTMOUSE;
Atom JOYSTICK;
};
struct kinc_x11_libs {
void *X11;
void *Xcursor;
void *Xi;
void *Xinerama;
void *Xrandr;
};
struct kinc_x11_procs {
Display *(*XOpenDisplay)(const char *name);
Status (*XInternAtoms)(Display *display, char **names, int count, Bool only_if_exists, Atom *atoms_return);
int (*XCloseDisplay)(Display *display);
XErrorHandler (*XSetErrorHandler)(XErrorHandler handler);
int (*XGetErrorText)(Display *, int, char *, int);
int (*XPending)(Display *display);
int (*XFlush)(Display *display);
int (*XNextEvent)(Display *display, XEvent *event_return);
int (*XPeekEvent)(Display *display, XEvent *event_return);
int (*XRefreshKeyboardMapping)(XMappingEvent *event_map);
int (*XwcLookupString)(XIC, XKeyPressedEvent *, wchar_t *, int, KeySym *, int *);
int (*XFilterEvent)(XEvent *, Window);
int (*XConvertSelection)(Display *, Atom, Atom, Atom, Window, Time);
int (*XSetSelectionOwner)(Display *, Atom, Window, Time);
int (*XLookupString)(XKeyEvent *, char *, int, KeySym *, XComposeStatus *);
KeySym (*XkbKeycodeToKeysym)(Display *, KeyCode, int, int);
int (*XSendEvent)(Display *, Window, int, long, XEvent *);
int (*XGetWindowProperty)(Display *, Window, Atom, long, long, int, Atom, Atom *, int *, unsigned long *, unsigned long *, unsigned char **);
int (*XFree)(void *);
int (*XChangeProperty)(Display *, Window, Atom, Atom, int, int, const unsigned char *, int);
int (*XDefineCursor)(Display *, Window, Cursor);
int (*XUndefineCursor)(Display *, Window);
Pixmap (*XCreateBitmapFromData)(Display *, Drawable, const char *, unsigned int, unsigned int);
Cursor (*XCreatePixmapCursor)(Display *, Pixmap, Pixmap, XColor *, XColor *, unsigned int, unsigned int);
int (*XFreePixmap)(Display *, Pixmap);
Cursor (*XcursorLibraryLoadCursor)(Display *, const char *);
int (*XWarpPointer)(Display *, Window, Window, int, int, unsigned int, unsigned int, int, int);
int (*XQueryPointer)(Display *, Window, Window *, Window *, int *, int *, int *, int *, unsigned int *);
Colormap (*XCreateColormap)(Display *, Window, Visual *, int);
Window (*XCreateWindow)(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth,
unsigned int class, Visual *visual, unsigned long valuemask, XSetWindowAttributes *attributes);
int (*XMoveWindow)(Display *, Window, int, int);
int (*XResizeWindow)(Display *, Window, unsigned int, unsigned int);
int (*XDestroyWindow)(Display *, Window);
int (*XSetClassHint)(Display *, Window, XClassHint *);
char *(*XSetLocaleModifiers)(const char *);
XIM (*XOpenIM)(Display *, struct _XrmHashBucketRec *, char *, char *);
int (*XCloseIM)(XIM);
XIC (*XCreateIC)(XIM, ...);
void (*XDestroyIC)(XIC);
void (*XSetICFocus)(XIC);
int (*XMapWindow)(Display *, Window);
int (*XUnmapWindow)(Display *, Window);
int (*XSetWMProtocols)(Display *, Window, Atom *, int);
XDeviceInfo *(*XListInputDevices)(Display *, int *);
void (*XFreeDeviceList)(XDeviceInfo *);
XDevice *(*XOpenDevice)(Display *display, XID device_id);
int (*XCloseDevice)(Display *display, XDevice *device);
int (*XSelectExtensionEvent)(Display *, Window, XEventClass *, int);
int (*XineramaQueryExtension)(Display *dpy, int *event_base, int *error_base);
int (*XineramaIsActive)(Display *dpy);
XineramaScreenInfo *(*XineramaQueryScreens)(Display *dpy, int *number);
XRRScreenResources *(*XRRGetScreenResourcesCurrent)(Display *dpy, Window window);
RROutput (*XRRGetOutputPrimary)(Display *dpy, Window window);
XRROutputInfo *(*XRRGetOutputInfo)(Display *dpy, XRRScreenResources *resources, RROutput output);
void (*XRRFreeOutputInfo)(XRROutputInfo *outputInfo);
XRRCrtcInfo *(*XRRGetCrtcInfo)(Display *dpy, XRRScreenResources *resources, RRCrtc crtc);
void (*XRRFreeCrtcInfo)(XRRCrtcInfo *crtcInfo);
void (*XRRFreeScreenResources)(XRRScreenResources *resources);
};
struct x11_pen_device {
XID id;
uint32_t motionEvent;
XEventClass motionClass;
uint32_t maxPressure;
float current_pressure;
void (*press)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/);
void (*move)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/);
void (*release)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/);
};
struct x11_context {
Display *display;
struct kinc_x11_libs libs;
struct kinc_x11_atoms atoms;
struct kinc_x11_mouse mouse;
struct x11_pen_device pen;
struct x11_pen_device eraser;
int num_windows;
struct kinc_x11_window windows[MAXIMUM_WINDOWS];
int num_displays;
struct kinc_x11_display displays[MAXIMUM_DISPLAYS];
};
struct kinc_x11_procs xlib;
struct x11_context x11_ctx;
void kinc_x11_copy_to_clipboard(const char *text);

View File

@ -0,0 +1,102 @@
#pragma once
#ifdef _WIN64
typedef __int64 INT_PTR;
typedef unsigned __int64 UINT_PTR;
typedef __int64 LONG_PTR;
typedef unsigned __int64 ULONG_PTR;
#else
typedef _W64 int INT_PTR;
typedef _W64 unsigned int UINT_PTR;
typedef _W64 long LONG_PTR;
typedef _W64 unsigned long ULONG_PTR;
#endif // WIN64
typedef unsigned long DWORD;
typedef DWORD *LPDWORD;
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define STD_ERROR_HANDLE ((DWORD)-12)
#define WINAPI __stdcall
typedef void *HWND;
typedef void *HANDLE;
typedef unsigned int UINT;
#define WINBASEAPI
typedef int BOOL;
#define CONST const
#define VOID void
typedef void *LPVOID;
typedef char CHAR;
typedef const CHAR *LPCSTR;
typedef wchar_t WCHAR;
typedef const WCHAR *LPCWSTR;
typedef CONST CHAR *LPCCH, *PCCH;
#define CP_UTF8 65001
typedef wchar_t WCHAR;
typedef WCHAR *LPWSTR;
typedef void *PVOID;
typedef long LONG;
typedef LONG *PLONG;
typedef CONST void *LPCVOID;
#define GENERIC_READ (0x80000000L)
#define GENERIC_WRITE (0x40000000L)
#define FILE_SHARE_READ 0x00000001
#define CREATE_ALWAYS 2
#define OPEN_EXISTING 3
#define FILE_ATTRIBUTE_NORMAL 0x00000080
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
#define FILE_BEGIN 0
#define FILE_CURRENT 1
#define MAX_PATH 260
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union {
struct {
DWORD Offset;
DWORD OffsetHigh;
} DUMMYSTRUCTNAME;
PVOID Pointer;
} DUMMYUNIONNAME;
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
WINBASEAPI BOOL WINAPI WriteConsoleA(HANDLE hConsoleOutput, CONST VOID *lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten,
LPVOID lpReserved);
WINBASEAPI BOOL WINAPI WriteConsoleW(HANDLE hConsoleOutput, CONST VOID *lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten,
LPVOID lpReserved);
WINBASEAPI VOID WINAPI OutputDebugStringA(LPCSTR lpOutputString);
WINBASEAPI VOID WINAPI OutputDebugStringW(LPCWSTR lpOutputString);
WINBASEAPI HANDLE WINAPI GetStdHandle(DWORD nStdHandle);
int WINAPI MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar);
WINBASEAPI HANDLE WINAPI CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
WINBASEAPI DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh);
WINBASEAPI BOOL WINAPI ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
WINBASEAPI DWORD WINAPI SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod);
WINBASEAPI BOOL WINAPI CloseHandle(HANDLE hObject);
WINBASEAPI BOOL WINAPI WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
int WINAPI MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);

View File

@ -0,0 +1,46 @@
#include "SystemMicrosoft.h"
#include <kinc/error.h>
#include <kinc/libs/stb_sprintf.h>
#define S_OK ((HRESULT)0L)
static void winerror(HRESULT result) {
LPVOID buffer = NULL;
DWORD dw = GetLastError();
__debugbreak();
#if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
if (dw != 0) {
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, NULL);
kinc_error_message("Error: %s", buffer);
}
else {
#endif
kinc_error_message("Unknown Windows error, return value was 0x%x.", result);
#if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
}
#endif
}
void kinc_microsoft_affirm(HRESULT result) {
if (result != S_OK) {
winerror(result);
}
}
void kinc_microsoft_affirm_message(HRESULT result, const char *format, ...) {
va_list args;
va_start(args, format);
kinc_affirm_args(result == S_OK, format, args);
va_end(args);
}
void kinc_microsoft_format(const char *format, va_list args, wchar_t *buffer) {
char cbuffer[4096];
vsprintf(cbuffer, format, args);
MultiByteToWideChar(CP_UTF8, 0, cbuffer, -1, buffer, 4096);
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef long HRESULT;
void kinc_microsoft_affirm(HRESULT result);
void kinc_microsoft_affirm_message(HRESULT result, const char *format, ...);
void kinc_microsoft_format(const char *format, va_list args, wchar_t *buffer);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,53 @@
#pragma once
#ifndef _WIN64
#include <kinc/error.h>
#endif
#include <intrin.h>
static inline bool kinc_atomic_compare_exchange(volatile int32_t *pointer, int32_t old_value, int32_t new_value) {
return _InterlockedCompareExchange((volatile long *)pointer, new_value, old_value) == old_value;
}
#define KINC_ATOMIC_COMPARE_EXCHANGE(pointer, oldValue, newValue) (kinc_atomic_compare_exchange(pointer, oldValue, newValue))
static inline bool kinc_atomic_compare_exchange_pointer(void *volatile *pointer, void *old_value, void *new_value) {
return _InterlockedCompareExchangePointer(pointer, new_value, old_value) == old_value;
}
#define KINC_ATOMIC_COMPARE_EXCHANGE_POINTER(pointer, oldValue, newValue) (kinc_atomic_compare_exchange_pointer(pointer, oldValue, newValue))
static inline int32_t kinc_atomic_increment(volatile int32_t *pointer) {
return _InterlockedIncrement((volatile long *)pointer) - 1;
}
#define KINC_ATOMIC_INCREMENT(pointer) (kinc_atomic_increment(pointer))
static inline int32_t kinc_atomic_decrement(volatile int32_t *pointer) {
return _InterlockedDecrement((volatile long *)pointer) + 1;
}
#define KINC_ATOMIC_DECREMENT(pointer) (kinc_atomic_decrement(pointer))
static inline void kinc_atomic_exchange(volatile int32_t *pointer, int32_t value) {
_InterlockedExchange((volatile long *)pointer, value);
}
#define KINC_ATOMIC_EXCHANGE_32(pointer, value) (kinc_atomic_exchange(pointer, value))
static inline void kinc_atomic_exchange_float(volatile float *pointer, float value) {
_InterlockedExchange((volatile long *)pointer, *(long *)&value);
}
#define KINC_ATOMIC_EXCHANGE_FLOAT(pointer, value) (kinc_atomic_exchange_float(pointer, value))
static inline void kinc_atomic_exchange_double(volatile double *pointer, double value) {
#ifdef _WIN64
_InterlockedExchange64((volatile __int64 *)pointer, *(__int64 *)&value);
#else
kinc_error_message("kinc_atomic_exchange_double is not supported for 32 bit Windows builds");
#endif
}
#define KINC_ATOMIC_EXCHANGE_DOUBLE(pointer, value) (kinc_atomic_exchange_double(pointer, value))

View File

@ -0,0 +1,25 @@
#include <kinc/threads/event.h>
void kinc_event_init(kinc_event_t *event, bool auto_clear) {
event->impl.event = CreateEvent(0, auto_clear ? FALSE : TRUE, 0, 0);
}
void kinc_event_destroy(kinc_event_t *event) {
CloseHandle(event->impl.event);
}
void kinc_event_signal(kinc_event_t *event) {
SetEvent(event->impl.event);
}
void kinc_event_wait(kinc_event_t *event) {
WaitForSingleObject(event->impl.event, INFINITE);
}
bool kinc_event_try_to_wait(kinc_event_t *event, double seconds) {
return WaitForSingleObject(event->impl.event, (DWORD)(seconds * 1000.0)) != WAIT_TIMEOUT;
}
void kinc_event_reset(kinc_event_t *event) {
ResetEvent(event->impl.event);
}

View File

@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void *event;
} kinc_event_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,35 @@
#include <kinc/threads/fiber.h>
VOID WINAPI fiber_func(LPVOID param) {
#ifndef KINC_WINDOWSAPP
kinc_fiber_t *fiber = (kinc_fiber_t *)param;
fiber->impl.func(fiber->impl.param);
#endif
}
void kinc_fiber_init_current_thread(kinc_fiber_t *fiber) {
#ifndef KINC_WINDOWSAPP
fiber->impl.fiber = ConvertThreadToFiber(NULL);
#endif
}
void kinc_fiber_init(kinc_fiber_t *fiber, void (*func)(void *param), void *param) {
#ifndef KINC_WINDOWSAPP
fiber->impl.func = func;
fiber->impl.param = param;
fiber->impl.fiber = CreateFiber(0, fiber_func, fiber);
#endif
}
void kinc_fiber_destroy(kinc_fiber_t *fiber) {
#ifndef KINC_WINDOWSAPP
DeleteFiber(fiber->impl.fiber);
fiber->impl.fiber = NULL;
#endif
}
void kinc_fiber_switch(kinc_fiber_t *fiber) {
#ifndef KINC_WINDOWSAPP
SwitchToFiber(fiber->impl.fiber);
#endif
}

View File

@ -0,0 +1,15 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void *fiber;
void (*func)(void *param);
void *param;
} kinc_fiber_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,56 @@
// Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define NOATOM
#define NOCLIPBOARD
#define NOCOLOR
#define NOCOMM
#define NOCTLMGR
#define NODEFERWINDOWPOS
#define NODRAWTEXT
#define NOGDI
#define NOGDICAPMASKS
#define NOHELP
#define NOICONS
#define NOKANJI
#define NOKEYSTATES
#define NOMB
#define NOMCX
#define NOMEMMGR
#define NOMENUS
#define NOMETAFILE
#define NOMINMAX
#define NOMSG
//#define NONLS
#define NOOPENFILE
#define NOPROFILER
#define NORASTEROPS
#define NOSCROLL
#define NOSERVICE
#define NOSHOWWINDOW
#define NOSOUND
#define NOSYSCOMMANDS
#define NOSYSMETRICS
#define NOTEXTMETRIC
#define NOUSER
#define NOVIRTUALKEYCODES
#define NOWH
#define NOWINMESSAGES
#define NOWINOFFSETS
#define NOWINSTYLES
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <assert.h>
#include <intrin.h>
#include <stdio.h>
#include "SystemMicrosoft.c.h"
#include "event.c.h"
#include "fiber.c.h"
#include "mutex.c.h"
#include "semaphore.c.h"
#include "thread.c.h"
#include "threadlocal.c.h"

View File

@ -0,0 +1,60 @@
#include <kinc/threads/mutex.h>
void kinc_mutex_init(kinc_mutex_t *mutex) {
assert(sizeof(RTL_CRITICAL_SECTION) == sizeof(kinc_microsoft_critical_section_t));
InitializeCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection);
}
void kinc_mutex_destroy(kinc_mutex_t *mutex) {
DeleteCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection);
}
void kinc_mutex_lock(kinc_mutex_t *mutex) {
EnterCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection);
}
bool kinc_mutex_try_to_lock(kinc_mutex_t *mutex) {
return TryEnterCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection);
}
void kinc_mutex_unlock(kinc_mutex_t *mutex) {
LeaveCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection);
}
bool kinc_uber_mutex_init(kinc_uber_mutex_t *mutex, const char *name) {
#if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
mutex->impl.id = (void *)CreateMutexA(NULL, FALSE, name);
HRESULT res = GetLastError();
if (res && res != ERROR_ALREADY_EXISTS) {
mutex->impl.id = NULL;
assert(false);
return false;
}
return true;
#else
return false;
#endif
}
void kinc_uber_mutex_destroy(kinc_uber_mutex_t *mutex) {
#if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
if (mutex->impl.id) {
CloseHandle((HANDLE)mutex->impl.id);
mutex->impl.id = NULL;
}
#endif
}
void kinc_uber_mutex_lock(kinc_uber_mutex_t *mutex) {
#if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
bool succ = WaitForSingleObject((HANDLE)mutex->impl.id, INFINITE) == WAIT_FAILED ? false : true;
assert(succ);
#endif
}
void kinc_uber_mutex_unlock(kinc_uber_mutex_t *mutex) {
#if defined(KINC_WINDOWS) || defined(KINC_WINDOWSAPP)
bool succ = ReleaseMutex((HANDLE)mutex->impl.id) == FALSE ? false : true;
assert(succ);
#endif
}

View File

@ -0,0 +1,26 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void *DebugInfo;
long LockCount;
long RecursionCount;
void *OwningThread;
void *LockSemaphore;
unsigned long __w64 SpinCount;
} kinc_microsoft_critical_section_t;
typedef struct {
kinc_microsoft_critical_section_t criticalSection;
} kinc_mutex_impl_t;
typedef struct {
void *id;
} kinc_uber_mutex_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,22 @@
#include <kinc/threads/semaphore.h>
void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max) {
semaphore->impl.handle = CreateSemaphoreA(NULL, current, max, NULL);
}
void kinc_semaphore_destroy(kinc_semaphore_t *semaphore) {
CloseHandle(semaphore->impl.handle);
semaphore->impl.handle = NULL;
}
void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count) {
ReleaseSemaphore(semaphore->impl.handle, count, NULL);
}
void kinc_semaphore_acquire(kinc_semaphore_t *semaphore) {
WaitForSingleObject(semaphore->impl.handle, INFINITE);
}
bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double seconds) {
return WaitForSingleObject(semaphore->impl.handle, (DWORD)(seconds * 1000)) == WAIT_OBJECT_0;
}

View File

@ -0,0 +1,5 @@
#pragma once
typedef struct {
void *handle;
} kinc_semaphore_impl_t;

View File

@ -0,0 +1,87 @@
#include <kinc/threads/thread.h>
#ifdef KINC_VTUNE
#include <ittnotify.h>
#endif
#ifdef KINC_SUPERLUMINAL
#include <Superluminal/PerformanceAPI_capi.h>
#endif
void kinc_threads_init() {}
void kinc_threads_quit() {}
struct thread_start {
void (*thread)(void *param);
void *param;
};
#define THREAD_STARTS 64
static struct thread_start starts[THREAD_STARTS];
static int thread_start_index = 0;
static DWORD WINAPI ThreadProc(LPVOID arg) {
intptr_t start_index = (intptr_t)arg;
starts[start_index].thread(starts[start_index].param);
return 0;
}
void kinc_thread_init(kinc_thread_t *thread, void (*func)(void *param), void *param) {
thread->impl.func = func;
thread->impl.param = param;
intptr_t start_index = thread_start_index++;
if (thread_start_index >= THREAD_STARTS) {
thread_start_index = 0;
}
starts[start_index].thread = func;
starts[start_index].param = param;
thread->impl.handle = CreateThread(0, 65536, ThreadProc, (LPVOID)start_index, 0, 0);
assert(thread->impl.handle != NULL);
}
void kinc_thread_wait_and_destroy(kinc_thread_t *thread) {
WaitForSingleObject(thread->impl.handle, INFINITE);
CloseHandle(thread->impl.handle);
}
bool kinc_thread_try_to_destroy(kinc_thread_t *thread) {
DWORD code;
GetExitCodeThread(thread->impl.handle, &code);
if (code != STILL_ACTIVE) {
CloseHandle(thread->impl.handle);
return true;
}
return false;
}
typedef HRESULT(WINAPI *SetThreadDescriptionType)(HANDLE hThread, PCWSTR lpThreadDescription);
static SetThreadDescriptionType MySetThreadDescription = NULL;
static bool set_thread_description_loaded = false;
void kinc_thread_set_name(const char *name) {
if (!set_thread_description_loaded) {
HMODULE kernel32 = LoadLibraryA("kernel32.dll");
MySetThreadDescription = (SetThreadDescriptionType)GetProcAddress(kernel32, "SetThreadDescription");
set_thread_description_loaded = true;
}
if (MySetThreadDescription != NULL) {
wchar_t wide_name[256];
MultiByteToWideChar(CP_ACP, 0, name, -1, wide_name, 256);
MySetThreadDescription(GetCurrentThread(), wide_name);
}
#ifdef KINC_VTUNE
__itt_thread_set_name(name);
#endif
#ifdef KINC_SUPERLUMINAL
PerformanceAPI_SetCurrentThreadName(name);
#endif
}
void kinc_thread_sleep(int milliseconds) {
Sleep(milliseconds);
}

View File

@ -0,0 +1,15 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void *handle;
void *param;
void (*func)(void *param);
} kinc_thread_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,18 @@
#include <kinc/threads/threadlocal.h>
void kinc_thread_local_init(kinc_thread_local_t *local) {
local->impl.slot = TlsAlloc();
TlsSetValue(local->impl.slot, 0);
}
void kinc_thread_local_destroy(kinc_thread_local_t *local) {
TlsFree(local->impl.slot);
}
void *kinc_thread_local_get(kinc_thread_local_t *local) {
return TlsGetValue(local->impl.slot);
}
void kinc_thread_local_set(kinc_thread_local_t *local, void *data) {
TlsSetValue(local->impl.slot, data);
}

View File

@ -0,0 +1,13 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int slot;
} kinc_thread_local_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,103 @@
#pragma once
#include <kinc/global.h>
#if defined(KINC_MACOS) || defined(KINC_IOS)
#include <libkern/OSAtomic.h>
static inline bool kinc_atomic_compare_exchange(volatile int32_t *pointer, int32_t old_value, int32_t new_value) {
return OSAtomicCompareAndSwap32Barrier(old_value, new_value, pointer);
}
static inline bool kinc_atomic_compare_exchange_pointer(void *volatile *pointer, void *old_value, void *new_value) {
return OSAtomicCompareAndSwapPtrBarrier(old_value, new_value, pointer);
}
static inline int32_t kinc_atomic_increment(volatile int32_t *pointer) {
return OSAtomicIncrement32Barrier(pointer) - 1;
}
static inline int32_t kinc_atomic_decrement(volatile int32_t *pointer) {
return OSAtomicDecrement32Barrier(pointer) + 1;
}
static inline void kinc_atomic_exchange(volatile int32_t *pointer, int32_t value) {
__sync_swap(pointer, value);
}
static inline void kinc_atomic_exchange_float(volatile float *pointer, float value) {
__sync_swap((volatile int32_t *)pointer, *(int32_t *)&value);
}
static inline void kinc_atomic_exchange_double(volatile double *pointer, double value) {
__sync_swap((volatile int64_t *)pointer, *(int64_t *)&value);
}
#else
// clang/gcc intrinsics
static inline bool kinc_atomic_compare_exchange(volatile int32_t *pointer, int32_t old_value, int32_t new_value) {
return __sync_val_compare_and_swap(pointer, old_value, new_value) == old_value;
}
static inline bool kinc_atomic_compare_exchange_pointer(void *volatile *pointer, void *old_value, void *new_value) {
return __sync_val_compare_and_swap(pointer, old_value, new_value) == old_value;
}
static inline int32_t kinc_atomic_increment(volatile int32_t *pointer) {
return __sync_fetch_and_add(pointer, 1);
}
static inline int32_t kinc_atomic_decrement(volatile int32_t *pointer) {
return __sync_fetch_and_sub(pointer, 1);
}
#ifdef __clang__
static inline void kinc_atomic_exchange(volatile int32_t *pointer, int32_t value) {
__sync_swap(pointer, value);
}
static inline void kinc_atomic_exchange_float(volatile float *pointer, float value) {
__sync_swap((volatile int32_t *)pointer, *(int32_t *)&value);
}
static inline void kinc_atomic_exchange_double(volatile double *pointer, double value) {
__sync_swap((volatile int64_t *)pointer, *(int64_t *)&value);
}
#else
// Beware, __sync_lock_test_and_set is not a full barrier and can have platform-specific weirdness
static inline void kinc_atomic_exchange(volatile int32_t *pointer, int32_t value) {
__sync_lock_test_and_set(pointer, value);
}
static inline void kinc_atomic_exchange_float(volatile float *pointer, float value) {
__sync_lock_test_and_set((volatile int32_t *)pointer, *(int32_t *)&value);
}
static inline void kinc_atomic_exchange_double(volatile double *pointer, double value) {
__sync_lock_test_and_set((volatile int64_t *)pointer, *(int64_t *)&value);
}
#endif
#endif
#define KINC_ATOMIC_COMPARE_EXCHANGE(pointer, oldValue, newValue) (kinc_atomic_compare_exchange(pointer, oldValue, newValue))
#define KINC_ATOMIC_COMPARE_EXCHANGE_POINTER(pointer, oldValue, newValue) (kinc_atomic_compare_exchange_pointer(pointer, oldValue, newValue))
#define KINC_ATOMIC_INCREMENT(pointer) (kinc_atomic_increment(pointer))
#define KINC_ATOMIC_DECREMENT(pointer) (kinc_atomic_decrement(pointer))
#define KINC_ATOMIC_EXCHANGE_32(pointer, value) (kinc_atomic_exchange(pointer, value))
#define KINC_ATOMIC_EXCHANGE_FLOAT(pointer, value) (kinc_atomic_exchange_float(pointer, value))
#define KINC_ATOMIC_EXCHANGE_DOUBLE(pointer, value) (kinc_atomic_exchange_double(pointer, value))

View File

@ -0,0 +1,78 @@
#include <kinc/threads/event.h>
#include <assert.h>
#include <errno.h>
#include <sys/time.h>
void kinc_event_init(kinc_event_t *event, bool auto_reset) {
event->impl.auto_reset = auto_reset;
event->impl.set = false;
pthread_cond_init(&event->impl.event, NULL);
// pthread_mutexattr_t attr;
// pthread_mutexattr_init(&attr);
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&event->impl.mutex, NULL); //&attr);
}
void kinc_event_destroy(kinc_event_t *event) {
pthread_cond_destroy(&event->impl.event);
pthread_mutex_destroy(&event->impl.mutex);
}
void kinc_event_signal(kinc_event_t *event) {
pthread_mutex_lock(&event->impl.mutex);
if (!event->impl.set) {
event->impl.set = true;
pthread_cond_signal(&event->impl.event);
}
pthread_mutex_unlock(&event->impl.mutex);
}
void kinc_event_wait(kinc_event_t *event) {
pthread_mutex_lock(&event->impl.mutex);
while (!event->impl.set) {
pthread_cond_wait(&event->impl.event, &event->impl.mutex);
}
if (event->impl.auto_reset) {
event->impl.set = false;
}
pthread_mutex_unlock(&event->impl.mutex);
}
bool kinc_event_try_to_wait(kinc_event_t *event, double seconds) {
pthread_mutex_lock(&event->impl.mutex);
struct timeval tv;
gettimeofday(&tv, 0);
int isec = (int)seconds;
int usec = (int)((seconds - isec) * 1000000.0);
struct timespec spec;
spec.tv_nsec = (tv.tv_usec + usec) * 1000;
if (spec.tv_nsec > 1000000000) {
spec.tv_nsec -= 1000000000;
isec += 1;
}
spec.tv_sec = tv.tv_sec + isec;
while (!event->impl.set) {
int result = pthread_cond_timedwait(&event->impl.event, &event->impl.mutex, &spec);
if (result == 0 || result == ETIMEDOUT) {
break;
}
}
bool result = event->impl.set;
if (event->impl.auto_reset) {
event->impl.set = false;
}
pthread_mutex_unlock(&event->impl.mutex);
return result;
}
void kinc_event_reset(kinc_event_t *event) {
pthread_mutex_lock(&event->impl.mutex);
event->impl.set = false;
pthread_mutex_unlock(&event->impl.mutex);
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <pthread.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
pthread_cond_t event;
pthread_mutex_t mutex;
volatile bool set;
bool auto_reset;
} kinc_event_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,40 @@
#include <kinc/threads/mutex.h>
#include <assert.h>
void kinc_mutex_init(kinc_mutex_t *mutex) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex->impl.mutex, &attr);
}
void kinc_mutex_destroy(kinc_mutex_t *mutex) {
pthread_mutex_destroy(&mutex->impl.mutex);
}
bool kinc_mutex_try_to_lock(kinc_mutex_t *mutex) {
return pthread_mutex_trylock(&mutex->impl.mutex) == 0;
}
void kinc_mutex_lock(kinc_mutex_t *mutex) {
pthread_mutex_lock(&mutex->impl.mutex);
}
void kinc_mutex_unlock(kinc_mutex_t *mutex) {
pthread_mutex_unlock(&mutex->impl.mutex);
}
bool kinc_uber_mutex_init(kinc_uber_mutex_t *mutex, const char *name) {
return false;
}
void kinc_uber_mutex_destroy(kinc_uber_mutex_t *mutex) {}
void kinc_uber_mutex_lock(kinc_uber_mutex_t *mutex) {
assert(false);
}
void kinc_uber_mutex_unlock(kinc_uber_mutex_t *mutex) {
assert(false);
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <pthread.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
pthread_mutex_t mutex;
} kinc_mutex_impl_t;
typedef struct {
int nothing;
} kinc_uber_mutex_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,5 @@
#include "event.c.h"
#include "mutex.c.h"
#include "semaphore.c.h"
#include "thread.c.h"
#include "threadlocal.c.h"

View File

@ -0,0 +1,56 @@
#include <kinc/system.h>
#include <kinc/threads/semaphore.h>
#ifdef __APPLE__
void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max) {
semaphore->impl.semaphore = dispatch_semaphore_create(current);
}
void kinc_semaphore_destroy(kinc_semaphore_t *semaphore) {}
void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count) {
for (int i = 0; i < count; ++i) {
dispatch_semaphore_signal(semaphore->impl.semaphore);
}
}
void kinc_semaphore_acquire(kinc_semaphore_t *semaphore) {
dispatch_semaphore_wait(semaphore->impl.semaphore, DISPATCH_TIME_FOREVER);
}
bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double seconds) {
return dispatch_semaphore_wait(semaphore->impl.semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * 1000 * 1000 * 1000))) == 0;
}
#else
void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max) {
sem_init(&semaphore->impl.semaphore, 0, current);
}
void kinc_semaphore_destroy(kinc_semaphore_t *semaphore) {
sem_destroy(&semaphore->impl.semaphore);
}
void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count) {
for (int i = 0; i < count; ++i) {
sem_post(&semaphore->impl.semaphore);
}
}
void kinc_semaphore_acquire(kinc_semaphore_t *semaphore) {
sem_wait(&semaphore->impl.semaphore);
}
bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double seconds) {
double now = kinc_time();
do {
if (sem_trywait(&semaphore->impl.semaphore) == 0) {
return true;
}
} while (kinc_time() < now + seconds);
return false;
}
#endif

View File

@ -0,0 +1,23 @@
#pragma once
#ifdef __APPLE__
#include <dispatch/dispatch.h>
#else
#include <semaphore.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
#ifdef __APPLE__
dispatch_semaphore_t semaphore;
#else
sem_t semaphore;
#endif
} kinc_semaphore_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,82 @@
#include <kinc/threads/thread.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#if !defined(KINC_IOS) && !defined(KINC_MACOS)
struct thread_start {
void (*thread)(void *param);
void *param;
};
#define THREAD_STARTS 64
static struct thread_start starts[THREAD_STARTS];
static int thread_start_index = 0;
static void *ThreadProc(void *arg) {
intptr_t start_index = (intptr_t)arg;
starts[start_index].thread(starts[start_index].param);
pthread_exit(NULL);
return NULL;
}
void kinc_thread_init(kinc_thread_t *t, void (*thread)(void *param), void *param) {
t->impl.param = param;
t->impl.thread = thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
// pthread_attr_setstacksize(&attr, 1024 * 64);
struct sched_param sp;
memset(&sp, 0, sizeof(sp));
sp.sched_priority = 0;
pthread_attr_setschedparam(&attr, &sp);
intptr_t start_index = thread_start_index++;
if (thread_start_index >= THREAD_STARTS) {
thread_start_index = 0;
}
starts[start_index].thread = thread;
starts[start_index].param = param;
int ret = pthread_create(&t->impl.pthread, &attr, &ThreadProc, (void *)start_index);
assert(ret == 0);
pthread_attr_destroy(&attr);
}
void kinc_thread_wait_and_destroy(kinc_thread_t *thread) {
int ret;
do {
ret = pthread_join(thread->impl.pthread, NULL);
} while (ret != 0);
}
bool kinc_thread_try_to_destroy(kinc_thread_t *thread) {
return pthread_join(thread->impl.pthread, NULL) == 0;
}
void kinc_threads_init() {}
void kinc_threads_quit() {}
#endif
#if !defined(KINC_IOS) && !defined(KINC_MACOS)
// Alternatively _GNU_SOURCE can be defined to make
// the headers declare it but let's not make it too
// easy to write Linux-specific POSIX-code
int pthread_setname_np(pthread_t thread, const char *name);
#endif
void kinc_thread_set_name(const char *name) {
#if !defined(KINC_IOS) && !defined(KINC_MACOS)
pthread_setname_np(pthread_self(), name);
#else
pthread_setname_np(name);
#endif
}
void kinc_thread_sleep(int milliseconds) {
usleep(1000 * (useconds_t)milliseconds);
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <pthread.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void *param;
void (*thread)(void *param);
pthread_t pthread;
} kinc_thread_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,17 @@
#include <kinc/threads/threadlocal.h>
void kinc_thread_local_init(kinc_thread_local_t *local) {
pthread_key_create(&local->impl.key, NULL);
}
void kinc_thread_local_destroy(kinc_thread_local_t *local) {
pthread_key_delete(local->impl.key);
}
void *kinc_thread_local_get(kinc_thread_local_t *local) {
return pthread_getspecific(local->impl.key);
}
void kinc_thread_local_set(kinc_thread_local_t *local, void *data) {
pthread_setspecific(local->impl.key, data);
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <pthread.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
pthread_key_t key;
} kinc_thread_local_impl_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,16 @@
#include <Kore/Graphics4/Texture.h>
#include <Kore/IO/FileReader.h>
namespace Kore {
class VideoSoundStream {
public:
VideoSoundStream(int nChannels, int freq) {}
void insertData(float *data, int nSamples) {}
float nextSample() {
return 0;
}
bool ended() {
return true;
}
};
}

View File

@ -0,0 +1,94 @@
#include <Kore/Log.h>
#include "Display.h"
#include <cstdio>
#include <cstdlib>
namespace Kore {
namespace Display {
void fatalError(const char *message) {
printf("main: %s\n", message);
exit(1);
}
enum { MAXIMUM_DISPLAY_COUNT = 10 };
DeviceInfo displays[MAXIMUM_DISPLAY_COUNT];
int displayCounter = -1;
bool initialized = false;
void enumDisplayMonitors(DeviceInfo screens[], int &displayCounter);
void enumerate() {
if (initialized) {
return;
}
initialized = true;
enumDisplayMonitors(displays, displayCounter);
}
int count() {
return displayCounter + 1;
}
int width(int index) {
return displays[index].width;
}
int height(int index) {
return displays[index].height;
}
int x(int index) {
return displays[index].x;
}
int y(int index) {
return displays[index].y;
}
bool isPrimary(int index) {
return displays[index].isPrimary;
}
const DeviceInfo *primaryScreen() {
for (int index = 0; index < MAXIMUM_DISPLAY_COUNT; ++index) {
const DeviceInfo &info = displays[index];
if (info.isAvailable && info.isPrimary) {
return &info;
}
}
if (!displays[0].isAvailable) {
log(Warning, "No display attached?");
// TODO (DK) throw exception?
return nullptr;
}
log(Warning, "No primary display defined, returning first display");
return &displays[0];
}
const DeviceInfo *screenById(int id) {
for (int index = 0; index < MAXIMUM_DISPLAY_COUNT; ++index) {
const DeviceInfo &info = displays[index];
if (info.number == id) {
return &info;
}
}
if (!displays[0].isAvailable) {
log(Warning, "No display available");
// TODO (DK) throw exception?
return nullptr;
}
log(Warning, "No display with id \"%i\" found, returning first display", id);
return &displays[0];
}
}
}

View File

@ -0,0 +1,34 @@
#pragma once
namespace Kore {
namespace Display {
struct DeviceInfo {
int number;
bool isAvailable;
char name[32];
int x;
int y;
int width;
int height;
bool isPrimary;
DeviceInfo() {
number = -1;
name[0] = 0;
isAvailable = false;
isPrimary = false;
}
};
void enumerate();
const DeviceInfo *primaryScreen();
const DeviceInfo *screenById(int id);
int height(int index);
int width(int index);
int x(int index);
int y(int index);
bool isPrimary(int index);
int count();
}
}

View File

@ -0,0 +1,53 @@
#include <Kore/Input/Mouse.h>
#include <Kore/Log.h>
#include <Kore/System.h>
using namespace Kore;
void Mouse::_lock(int windowId, bool truth) {
show(!truth);
if (truth) {
int width = System::windowWidth(windowId);
int height = System::windowHeight(windowId);
int x, y;
getPosition(windowId, x, y);
// Guess the new position of X and Y
int newX = x;
int newY = y;
// Correct the position of the X coordinate
// if the mouse is out the window
if (x < 0) {
newX -= x;
}
else if (x > width) {
newX -= x - width;
}
// Correct the position of the Y coordinate
// if the mouse is out the window
if (y < 0) {
newY -= y;
}
else if (y > height) {
newY -= y - height;
}
// Force the mouse to stay inside the window
setPosition(windowId, newX, newY);
}
}
bool Mouse::canLock(int windowId) {
return true;
}
void Mouse::show(bool truth) {
// TODO
}
void Mouse::setPosition(int windowId, int x, int y) {}
void Mouse::getPosition(int windowId, int &x, int &y) {}

View File

@ -0,0 +1,204 @@
#include <Kore/Audio2/Audio.h>
#include <alsa/asoundlib.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
// apt-get install libasound2-dev
using namespace Kore;
namespace {
pthread_t threadid;
bool audioRunning = false;
snd_pcm_t *playback_handle;
const int bufferSize = 4096 * 4;
short buf[bufferSize];
void copySample(void *buffer) {
float value = *(float *)&Audio2::buffer.data[Audio2::buffer.readLocation];
Audio2::buffer.readLocation += 4;
if (Audio2::buffer.readLocation >= Audio2::buffer.dataSize)
Audio2::buffer.readLocation = 0;
if (value != 0) {
int a = 3;
++a;
}
*(s16 *)buffer = static_cast<s16>(value * 32767);
}
int playback_callback(snd_pcm_sframes_t nframes) {
int err = 0;
if (Kore::Audio2::audioCallback != nullptr) {
Kore::Audio2::audioCallback(nframes * 2);
int ni = 0;
while (ni < nframes) {
int i = 0;
for (; ni < nframes && i < bufferSize / 2; ++i, ++ni) {
copySample(&buf[i * 2]);
copySample(&buf[i * 2 + 1]);
}
int err2;
if ((err2 = snd_pcm_writei(playback_handle, buf, i)) < 0) {
// EPIPE is an underrun
fprintf(stderr, "write failed (%s)\n", snd_strerror(err2));
int recovered = snd_pcm_recover(playback_handle, err2, 1);
printf("Recovered: %d\n", recovered);
}
else
err += err2;
}
}
return err;
}
void *doAudio(void *arg) {
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
snd_pcm_sframes_t frames_to_deliver;
// int nfds;
int err;
// struct pollfd *pfds;
if ((err = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf(stderr, "cannot open audio device default (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_any(playback_handle, hw_params)) < 0) {
fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err));
exit(1);
}
uint rate = 44100;
int dir = 0;
if ((err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, &dir)) < 0) {
fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2)) < 0) {
fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err));
exit(1);
}
snd_pcm_uframes_t bufferSize = rate / 8;
if (((err = snd_pcm_hw_params_set_buffer_size(playback_handle, hw_params, bufferSize)) < 0 &&
(snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &bufferSize)) < 0)) {
fprintf(stderr, "cannot set buffer size (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_hw_params(playback_handle, hw_params)) < 0) {
fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err));
exit(1);
}
snd_pcm_hw_params_free(hw_params);
/* tell ALSA to wake us up whenever 4096 or more frames
of playback data can be delivered. Also, tell
ALSA that we'll start the device ourselves.
*/
if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {
fprintf(stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_sw_params_current(playback_handle, sw_params)) < 0) {
fprintf(stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_sw_params_set_avail_min(playback_handle, sw_params, 4096)) < 0) {
fprintf(stderr, "cannot set minimum available count (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_sw_params_set_start_threshold(playback_handle, sw_params, 0U)) < 0) {
fprintf(stderr, "cannot set start mode (%s)\n", snd_strerror(err));
exit(1);
}
if ((err = snd_pcm_sw_params(playback_handle, sw_params)) < 0) {
fprintf(stderr, "cannot set software parameters (%s)\n", snd_strerror(err));
exit(1);
}
/* the interface will interrupt the kernel every 4096 frames, and ALSA
will wake up this program very soon after that.
*/
if ((err = snd_pcm_prepare(playback_handle)) < 0) {
fprintf(stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err));
exit(1);
}
while (audioRunning) {
/* wait till the interface is ready for data, or 1 second
has elapsed.
*/
if ((err = snd_pcm_wait(playback_handle, 1000)) < 0) {
fprintf(stderr, "poll failed (%s)\n", strerror(errno));
break;
}
/* find out how much space is available for playback data */
if ((frames_to_deliver = snd_pcm_avail_update(playback_handle)) < 0) {
if (frames_to_deliver == -EPIPE) {
fprintf(stderr, "an xrun occured\n");
break;
}
else {
fprintf(stderr, "unknown ALSA avail update return value (%i)\n", (int)frames_to_deliver);
break;
}
}
// frames_to_deliver = frames_to_deliver > 4096 ? 4096 : frames_to_deliver;
/* deliver the data */
if (playback_callback(frames_to_deliver) != frames_to_deliver) {
fprintf(stderr, "playback callback failed\n");
// break; // Do not break so we can recover from errors.
}
}
snd_pcm_close(playback_handle);
return nullptr;
}
}
void Audio2::init() {
buffer.readLocation = 0;
buffer.writeLocation = 0;
buffer.dataSize = 128 * 1024;
buffer.data = new u8[buffer.dataSize];
audioRunning = true;
pthread_create(&threadid, nullptr, &doAudio, nullptr);
}
void Audio2::update() {}
void Audio2::shutdown() {
audioRunning = false;
}

View File

@ -0,0 +1,602 @@
#include <Kore/Graphics4/Graphics.h>
#include <Kore/Input/Keyboard.h>
#include <Kore/Input/Mouse.h>
#include <Kore/Log.h>
#include <Kore/System.h>
#include "Display.h"
#include <assert.h>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
#include <Kore/ogl.h>
#include <bcm_host.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/input.h>
#include <X11/Xlib.h>
// apt-get install libx11-dev
namespace {
// static int snglBuf[] = {GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_STENCIL_SIZE, 8, None};
// static int dblBuf[] = {GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_STENCIL_SIZE, 8, GLX_DOUBLEBUFFER, None};
uint32_t screen_width;
uint32_t screen_height;
Kore::WindowMode windowMode;
uint32_t width;
uint32_t height;
bool bcmHostStarted = false;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
DISPMANX_ELEMENT_HANDLE_T dispman_element;
GLboolean doubleBuffer = GL_TRUE;
void fatalError(const char *message) {
printf("main: %s\n", message);
exit(1);
}
int getbit(uint32_t *bits, uint32_t bit) {
return (bits[bit / 32] >> (bit % 32)) & 1;
}
void enable_bit(uint32_t *bits, uint32_t bit) {
bits[bit / 32] |= 1u << (bit % 32);
}
void disable_bit(uint32_t *bits, uint32_t bit) {
bits[bit / 32] &= ~(1u << (bit % 32));
}
void set_bit(uint32_t *bits, uint32_t bit, int value) {
if (value)
enable_bit(bits, bit);
else
disable_bit(bits, bit);
}
struct InputDevice {
int fd;
uint16_t keys;
uint32_t key_state[(KEY_MAX - 1) / 32 + 1];
};
const int inputDevicesCount = 16;
InputDevice inputDevices[inputDevicesCount];
Display *dpy;
void openXWindow(int x, int y, int width, int height) {
dpy = XOpenDisplay(NULL);
XSetWindowAttributes winAttr;
winAttr.event_mask = StructureNotifyMask;
Window w = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, width, height, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWEventMask, &winAttr);
XMapWindow(dpy, w);
XFlush(dpy);
// TODO: Figure this out
/*Atom atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", True);
Atom atom2;
int f;
unsigned long n, b;
XEvent e;
unsigned char *data = 0;
while (XGetWindowProperty(dpy, w, atom,
0, 4, False, AnyPropertyType,
&atom2, &f,
&n, &b, &data) != Success || n != 4 || b != 0) {
XNextEvent(dpy, &e);
}
long* extents = (long*) data;
//printf ("Got frame extents: left %ld right %ld top %ld bottom %ld\n",
// extents[0], extents[1], extents[2], extents[3]);
*/
XMoveWindow(dpy, w, x - 1, y - 30); // extents[2]);
XFlush(dpy);
}
void setScreenSize() {
if (!bcmHostStarted) {
bcm_host_init();
bcmHostStarted = true;
}
if (screen_width == 0 || screen_height == 0) {
int32_t success = 0;
success = graphics_get_display_size(0 /* LCD */, &screen_width, &screen_height);
assert(success >= 0);
}
}
}
namespace Kore {
namespace Display {
void enumDisplayMonitors(DeviceInfo screens[], int &displayCounter) {
displayCounter = 1;
}
}
}
void Kore::System::setup() {}
bool Kore::System::isFullscreen() {
// TODO (DK)
return false;
}
// TODO (DK) the whole glx stuff should go into Graphics/OpenGL?
// -then there would be a better separation between window + context setup
int createWindow(const char *title, int x, int y, int width, int height, Kore::WindowMode windowMode, int targetDisplay, int depthBufferBits,
int stencilBufferBits) {
if (!bcmHostStarted) {
bcm_host_init();
bcmHostStarted = true;
}
::windowMode = windowMode;
::width = width;
::height = height;
// uint32_t screen_width = 640;
// uint32_t screen_height = 480;
EGLBoolean result;
EGLint num_config;
static EGL_DISPMANX_WINDOW_T nativewindow;
DISPMANX_DISPLAY_HANDLE_T dispman_display;
DISPMANX_UPDATE_HANDLE_T dispman_update;
VC_RECT_T dst_rect;
VC_RECT_T src_rect;
static const EGLint attribute_list[] = {EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE,
EGL_WINDOW_BIT, EGL_NONE};
static const EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
EGLConfig config;
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert(display != EGL_NO_DISPLAY);
glCheckErrors();
result = eglInitialize(display, NULL, NULL);
assert(EGL_FALSE != result);
glCheckErrors();
result = eglChooseConfig(display, attribute_list, &config, 1, &num_config);
assert(EGL_FALSE != result);
glCheckErrors();
result = eglBindAPI(EGL_OPENGL_ES_API);
assert(EGL_FALSE != result);
glCheckErrors();
context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes);
assert(context != EGL_NO_CONTEXT);
glCheckErrors();
setScreenSize();
if (windowMode == Kore::WindowModeFullscreen) {
dst_rect.x = 0;
dst_rect.y = 0;
dst_rect.width = screen_width;
dst_rect.height = screen_height;
src_rect.x = 0;
src_rect.y = 0;
src_rect.width = screen_width << 16;
src_rect.height = screen_height << 16;
}
else {
dst_rect.x = x;
dst_rect.y = y;
#ifdef KINC_RASPBERRY_PI_SCALING
dst_rect.width = screen_width;
dst_rect.height = screen_height;
#else
dst_rect.width = width;
dst_rect.height = height;
#endif
src_rect.x = 0;
src_rect.y = 0;
src_rect.width = width << 16;
src_rect.height = height << 16;
}
dispman_display = vc_dispmanx_display_open(0 /* LCD */);
dispman_update = vc_dispmanx_update_start(0);
VC_DISPMANX_ALPHA_T alpha = {};
alpha.flags = (DISPMANX_FLAGS_ALPHA_T)(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS);
alpha.opacity = 255;
alpha.mask = 0;
dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0 /*layer*/, &dst_rect, 0 /*src*/, &src_rect, DISPMANX_PROTECTION_NONE, &alpha,
0 /*clamp*/, (DISPMANX_TRANSFORM_T)0 /*transform*/);
nativewindow.element = dispman_element;
if (windowMode == Kore::WindowModeFullscreen) {
nativewindow.width = screen_width;
nativewindow.height = screen_height;
}
else {
nativewindow.width = width;
nativewindow.height = height;
#ifndef KINC_RASPBERRY_PI_SCALING
openXWindow(x, y, width, height);
#endif
}
vc_dispmanx_update_submit_sync(dispman_update);
glCheckErrors();
surface = eglCreateWindowSurface(display, config, &nativewindow, NULL);
assert(surface != EGL_NO_SURFACE);
glCheckErrors();
result = eglMakeCurrent(display, surface, surface, context);
assert(EGL_FALSE != result);
glCheckErrors();
// input
char name[64];
for (int i = 0; i < inputDevicesCount; ++i) {
sprintf(name, "/dev/input/event%d", i);
// PIGU_device_info_t info;
// if(PIGU_detect_device(name, &info) < 0)
uint32_t events[(KEY_MAX - 1) / 32 + 1];
inputDevices[i].fd = open(name, O_RDONLY | O_NONBLOCK);
if (inputDevices[i].fd < 0)
continue;
char deviceName[128];
ioctl(inputDevices[i].fd, EVIOCGNAME(sizeof(deviceName)), deviceName);
printf("Found a device. %s\n", deviceName);
uint32_t types[EV_MAX];
memset(types, 0, sizeof(types));
ioctl(inputDevices[i].fd, EVIOCGBIT(0, EV_MAX), types);
int keycount = 0;
if (getbit(types, EV_KEY)) {
// count events
memset(events, 0, sizeof(events));
ioctl(inputDevices[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), events);
int j = 0;
for (; j < BTN_MISC; ++j)
if (getbit(events, j))
keycount++;
/*j = BTN_MOUSE; // skip misc buttons
for(;j<BTN_JOYSTICK;++j)
if(PIGU_get_bit(events, j))
{
mouse_button_count++;
if(j-BTN_MOUSE>=16)
continue;
buttons.map[buttons.count] = j-BTN_MOUSE;
buttons.count++;
}
for(;j<BTN_GAMEPAD;++j)
if(PIGU_get_bit(events, j))
{
joystick_button_count++;
if(j-BTN_JOYSTICK>=16)
continue;
buttons.map[buttons.count] = j-BTN_JOYSTICK;
buttons.count++;
}
for(;j<BTN_DIGI;++j)
if(PIGU_get_bit(events, j))
{
gamepad_button_count++;
if(j-BTN_GAMEPAD>=16)
continue;
buttons.map[buttons.count] = j-BTN_GAMEPAD;
buttons.count++;
}*/
}
}
return 0;
}
namespace Kore {
namespace System {
int windowCount() {
return 1;
}
int windowWidth(int id) {
if (windowMode == Kore::WindowModeFullscreen) {
return screen_width;
}
else {
return width;
}
}
int windowHeight(int id) {
if (windowMode == Kore::WindowModeFullscreen) {
return screen_height;
}
else {
return height;
}
}
int desktopWidth() {
setScreenSize();
return screen_width;
}
int desktopHeight() {
setScreenSize();
return screen_height;
}
int initWindow(WindowOptions options) {
char buffer[1024] = {0};
strcat(buffer, name());
if (options.title != nullptr) {
strcat(buffer, options.title);
}
int id = createWindow(buffer, options.x == -1 ? screen_width / 2 - options.width / 2 : options.x,
options.y == -1 ? screen_height / 2 - options.height / 2 : options.y, options.width, options.height, options.mode,
options.targetDisplay, options.rendererOptions.depthBufferBits, options.rendererOptions.stencilBufferBits);
Graphics4::init(id, options.rendererOptions.depthBufferBits, options.rendererOptions.stencilBufferBits);
return id;
}
void *windowHandle(int id) {
return nullptr;
}
}
}
namespace Kore {
namespace System {
int currentDeviceId = -1;
int currentDevice() {
return currentDeviceId;
}
void setCurrentDevice(int id) {
currentDeviceId = id;
}
}
}
bool Kore::System::handleMessages() {
for (int i = 0; i < inputDevicesCount; ++i) {
int eventcount = 0;
if (inputDevices[i].fd < 0)
continue;
input_event event;
ssize_t readsize = read(inputDevices[i].fd, &event, sizeof(event));
while (readsize >= 0) {
set_bit(inputDevices[i].key_state, event.code, event.value);
#define KEY(linuxkey, korekey, keychar) \
case linuxkey: \
if (event.value == 1) \
Kore::Keyboard::the()->_keydown(korekey); \
else if (event.value == 0) \
Kore::Keyboard::the()->_keyup(korekey); \
break;
switch (event.code) {
KEY(KEY_RIGHT, KeyRight, ' ')
KEY(KEY_LEFT, KeyLeft, ' ')
KEY(KEY_UP, KeyUp, ' ')
KEY(KEY_DOWN, KeyDown, ' ')
KEY(KEY_SPACE, KeySpace, ' ')
KEY(KEY_BACKSPACE, KeyBackspace, ' ')
KEY(KEY_TAB, KeyTab, ' ')
// KEY(KEY_ENTER, KeyEnter, ' ')
KEY(KEY_LEFTSHIFT, KeyShift, ' ')
KEY(KEY_RIGHTSHIFT, KeyShift, ' ')
KEY(KEY_LEFTCTRL, KeyControl, ' ')
KEY(KEY_RIGHTCTRL, KeyControl, ' ')
KEY(KEY_LEFTALT, KeyAlt, ' ')
KEY(KEY_RIGHTALT, KeyAlt, ' ')
KEY(KEY_DELETE, KeyDelete, ' ')
KEY(KEY_A, KeyA, 'a')
KEY(KEY_B, KeyB, 'b')
KEY(KEY_C, KeyC, 'c')
KEY(KEY_D, KeyD, 'd')
KEY(KEY_E, KeyE, 'e')
KEY(KEY_F, KeyF, 'f')
KEY(KEY_G, KeyG, 'g')
KEY(KEY_H, KeyH, 'h')
KEY(KEY_I, KeyI, 'i')
KEY(KEY_J, KeyJ, 'j')
KEY(KEY_K, KeyK, 'k')
KEY(KEY_L, KeyL, 'l')
KEY(KEY_M, KeyM, 'm')
KEY(KEY_N, KeyN, 'n')
KEY(KEY_O, KeyO, 'o')
KEY(KEY_P, KeyP, 'p')
KEY(KEY_Q, KeyQ, 'q')
KEY(KEY_R, KeyR, 'r')
KEY(KEY_S, KeyS, 's')
KEY(KEY_T, KeyT, 't')
KEY(KEY_U, KeyU, 'u')
KEY(KEY_V, KeyV, 'v')
KEY(KEY_W, KeyW, 'w')
KEY(KEY_X, KeyX, 'x')
KEY(KEY_Y, KeyY, 'y')
KEY(KEY_Z, KeyZ, 'z')
KEY(KEY_1, Key1, '1')
KEY(KEY_2, Key2, '2')
KEY(KEY_3, Key3, '3')
KEY(KEY_4, Key4, '4')
KEY(KEY_5, Key5, '5')
KEY(KEY_6, Key6, '6')
KEY(KEY_7, Key7, '7')
KEY(KEY_8, Key8, '8')
KEY(KEY_9, Key9, '9')
KEY(KEY_0, Key0, '0')
}
#undef KEY
// printf("Code %d Value %d\n", event.code, event.value);
readsize = read(inputDevices[i].fd, &event, sizeof(event));
}
}
#ifndef KINC_RASPBERRY_PI_SCALING
if (windowMode != Kore::WindowModeFullscreen) {
while (XPending(dpy) > 0) {
XEvent event;
XNextEvent(dpy, &event);
printf("Got an X event.\n");
switch (event.type) {
case ConfigureNotify:
DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
VC_RECT_T dst_rect;
VC_RECT_T src_rect;
dst_rect.x = event.xconfigure.x;
dst_rect.y = event.xconfigure.y;
dst_rect.width = width;
dst_rect.height = height;
src_rect.x = 0;
src_rect.y = 0;
src_rect.width = width << 16;
src_rect.height = height << 16;
vc_dispmanx_element_change_attributes(update, dispman_element, 0, 0, 255, &dst_rect, &src_rect, 0, (DISPMANX_TRANSFORM_T)0);
vc_dispmanx_update_submit_sync(update);
}
}
}
#endif
return true;
}
const char *Kore::System::systemId() {
return "Pi";
}
void Kore::System::makeCurrent(int contextId) {
if (currentDeviceId == contextId) {
return;
}
#if !defined(NDEBUG)
// log(Info, "Kore/System | context switch from %i to %i", currentDeviceId, contextId);
#endif
currentDeviceId = contextId;
}
void Kore::Graphics4::clearCurrent() {}
void Kore::System::clearCurrent() {
#if !defined(NDEBUG)
// log(Info, "Kore/System | context clear");
#endif
currentDeviceId = -1;
Graphics4::clearCurrent();
}
void Kore::System::swapBuffers(int contextId) {
eglSwapBuffers(display, surface);
}
void Kore::System::destroyWindow(int id) {
// TODO (DK) implement me
}
void Kore::System::changeResolution(int width, int height, bool fullscreen) {}
void Kore::System::setTitle(const char *title) {}
void Kore::System::setKeepScreenOn(bool on) {}
void Kore::System::showWindow() {}
void Kore::System::showKeyboard() {}
void Kore::System::hideKeyboard() {}
void Kore::System::loadURL(const char *url) {}
void Kore::System::vibrate(int ms) {}
const char *Kore::System::language() {
return "en";
}
namespace {
char save[2000];
bool saveInitialized = false;
}
const char *Kore::System::savePath() {
if (!saveInitialized) {
strcpy(save, "Ķ~/.");
strcat(save, name());
strcat(save, "/");
saveInitialized = true;
}
return save;
}
namespace {
const char *videoFormats[] = {"ogv", nullptr};
}
const char **Kore::System::videoFormats() {
return ::videoFormats;
}
#include <sys/time.h>
#include <time.h>
double Kore::System::frequency() {
return 1000000.0;
}
Kore::System::ticks Kore::System::timestamp() {
timeval now;
gettimeofday(&now, NULL);
return static_cast<ticks>(now.tv_sec) * 1000000 + static_cast<ticks>(now.tv_usec);
}
extern int kore(int argc, char **argv);
int main(int argc, char **argv) {
kore(argc, argv);
}

Some files were not shown because too many files have changed in this diff Show More