forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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 */
|
@ -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
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
#include "audio.c.h"
|
||||
#include "display.c.h"
|
||||
#include "system.c.h"
|
||||
#include "window.c.h"
|
||||
#include "video.c.h"
|
133
Kha/Kinc/Backends/System/Android/Sources/kinc/backend/audio.c.h
Normal file
133
Kha/Kinc/Backends/System/Android/Sources/kinc/backend/audio.c.h
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
1306
Kha/Kinc/Backends/System/Android/Sources/kinc/backend/system.c.h
Normal file
1306
Kha/Kinc/Backends/System/Android/Sources/kinc/backend/system.c.h
Normal file
File diff suppressed because it is too large
Load Diff
581
Kha/Kinc/Backends/System/Android/Sources/kinc/backend/video.c.h
Normal file
581
Kha/Kinc/Backends/System/Android/Sources/kinc/backend/video.c.h
Normal 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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -0,0 +1 @@
|
||||
#pragma once
|
@ -0,0 +1,4 @@
|
||||
#include "http.m.h"
|
||||
#include "system.m.h"
|
||||
#include "thread.m.h"
|
||||
#include "video.m.h"
|
53
Kha/Kinc/Backends/System/Apple/Sources/kinc/backend/http.m.h
Normal file
53
Kha/Kinc/Backends/System/Apple/Sources/kinc/backend/http.m.h
Normal 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];
|
||||
}
|
@ -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
|
@ -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) {}
|
56
Kha/Kinc/Backends/System/Apple/Sources/kinc/backend/video.h
Normal file
56
Kha/Kinc/Backends/System/Apple/Sources/kinc/backend/video.h
Normal 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
|
311
Kha/Kinc/Backends/System/Apple/Sources/kinc/backend/video.m.h
Normal file
311
Kha/Kinc/Backends/System/Apple/Sources/kinc/backend/video.m.h
Normal 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;
|
||||
}
|
@ -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)
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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) {}
|
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int nothing;
|
||||
} kinc_event_impl_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -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) {}
|
@ -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"
|
@ -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) {}
|
@ -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) {}
|
@ -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
|
@ -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;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int nothing;
|
||||
} kinc_semaphore_impl_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -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);
|
||||
}
|
@ -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) {}
|
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int nothing;
|
||||
} kinc_thread_impl_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -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) {}
|
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int nothing;
|
||||
} kinc_thread_local_impl_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace Kore {
|
||||
struct WindowData {
|
||||
int width, height, mode;
|
||||
WindowData();
|
||||
};
|
||||
}
|
@ -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) {}
|
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace Kore {
|
||||
void initHIDGamepads();
|
||||
void updateHIDGamepads();
|
||||
void closeHIDGamepads();
|
||||
}
|
@ -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();
|
||||
}
|
67
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/funcs.h
Normal file
67
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/funcs.h
Normal 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();
|
198
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/gamepad.c.h
Normal file
198
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/gamepad.c.h
Normal 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) {}
|
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void kinc_linux_initHIDGamepads();
|
||||
void kinc_linux_updateHIDGamepads();
|
||||
void kinc_linux_closeHIDGamepads();
|
161
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/linuxunit.c
Normal file
161
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/linuxunit.c
Normal 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"
|
@ -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);
|
||||
}
|
233
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/sound.c.h
Normal file
233
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/sound.c.h
Normal 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;
|
||||
}
|
433
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/system.c.h
Normal file
433
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/system.c.h
Normal 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
|
||||
}
|
@ -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
|
31
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/video.h
Normal file
31
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/video.h
Normal 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
|
@ -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
@ -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))
|
@ -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);
|
@ -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;
|
||||
}
|
140
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/window.c.h
Normal file
140
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/window.c.h
Normal 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);
|
||||
}
|
@ -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;
|
||||
}
|
1055
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/x11/system.c.h
Normal file
1055
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/x11/system.c.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
200
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/x11/x11.h
Normal file
200
Kha/Kinc/Backends/System/Linux/Sources/kinc/backend/x11/x11.h
Normal 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);
|
@ -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);
|
@ -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);
|
||||
}
|
@ -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
|
@ -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))
|
@ -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);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
void *event;
|
||||
} kinc_event_impl_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -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
|
||||
}
|
@ -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
|
@ -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"
|
@ -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
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct {
|
||||
void *handle;
|
||||
} kinc_semaphore_impl_t;
|
@ -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);
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int slot;
|
||||
} kinc_thread_local_impl_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
103
Kha/Kinc/Backends/System/POSIX/Sources/kinc/backend/atomic.h
Normal file
103
Kha/Kinc/Backends/System/POSIX/Sources/kinc/backend/atomic.h
Normal 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))
|
@ -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);
|
||||
}
|
18
Kha/Kinc/Backends/System/POSIX/Sources/kinc/backend/event.h
Normal file
18
Kha/Kinc/Backends/System/POSIX/Sources/kinc/backend/event.h
Normal 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
|
@ -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);
|
||||
}
|
19
Kha/Kinc/Backends/System/POSIX/Sources/kinc/backend/mutex.h
Normal file
19
Kha/Kinc/Backends/System/POSIX/Sources/kinc/backend/mutex.h
Normal 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
|
@ -0,0 +1,5 @@
|
||||
#include "event.c.h"
|
||||
#include "mutex.c.h"
|
||||
#include "semaphore.c.h"
|
||||
#include "thread.c.h"
|
||||
#include "threadlocal.c.h"
|
@ -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
|
@ -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
|
@ -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);
|
||||
}
|
17
Kha/Kinc/Backends/System/POSIX/Sources/kinc/backend/thread.h
Normal file
17
Kha/Kinc/Backends/System/POSIX/Sources/kinc/backend/thread.h
Normal 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
|
@ -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);
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
94
Kha/Kinc/Backends/System/Pi/Sources/kinc/backend/display.cpp
Normal file
94
Kha/Kinc/Backends/System/Pi/Sources/kinc/backend/display.cpp
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
34
Kha/Kinc/Backends/System/Pi/Sources/kinc/backend/display.h
Normal file
34
Kha/Kinc/Backends/System/Pi/Sources/kinc/backend/display.h
Normal 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();
|
||||
}
|
||||
}
|
@ -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) {}
|
204
Kha/Kinc/Backends/System/Pi/Sources/kinc/backend/sound.cpp
Normal file
204
Kha/Kinc/Backends/System/Pi/Sources/kinc/backend/sound.cpp
Normal 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;
|
||||
}
|
602
Kha/Kinc/Backends/System/Pi/Sources/kinc/backend/system.cpp
Normal file
602
Kha/Kinc/Backends/System/Pi/Sources/kinc/backend/system.cpp
Normal 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
Reference in New Issue
Block a user