apps/fplay: major player updates, performance, usability and UI improvements, bugfixes (#560)
Very briefly: - feat: add playback timer and MP3 album art support - feat: async file reading with adaptive cache for smoother playback - feat: smart frame skipping (preserves sync, prevents lags on slow drives) - feat: add OSD volume and Tab info overlay - feat: mouse wheel volume control, and basic hotkeys - feat: support >2GB files, automatic HW acceleration check - fix: crash/memory corruption on videos without audio or with unknown duration - fix: freeze issues and audio-video desync after seeking - fix: correct playback speed for silent videos and playback of files with corrupted timestamps - fix: auto-hide volume slider in narrow windows and restore window controls layout --------- Co-authored-by: Burer <burer@kolibrios.org> Reviewed-on: KolibriOS/kolibrios#560 Reviewed-by: Burer <burer@kolibrios.org> Reviewed-by: Mikhail Frolov <mixa.frolov2003@gmail.com> Co-authored-by: leency <lipatov.kiril@gmail.com>
This commit is contained in:
1 parent
bb58df2d87
commit
8c48f1d18b
29 files changed
+3265
-593
No files matched your search
@@ -0,0 +1,138 @@
|
||||
/*OUTPUT_FORMAT("binary")*/
|
||||
/* SEARCH_DIR("=/lib") */
|
||||
/* SEARCH_DIR("=/mingw32/lib") */
|
||||
ENTRY(__start)
|
||||
SECTIONS
|
||||
{
|
||||
.text 0x000000:
|
||||
{
|
||||
LONG(0x554e454D);
|
||||
LONG(0x32305445);
|
||||
LONG(1);
|
||||
LONG(__start);
|
||||
LONG(___iend);
|
||||
LONG(___memsize);
|
||||
LONG(___stacktop);
|
||||
LONG(___cmdline);
|
||||
LONG(___pgmname); /* full path */
|
||||
LONG(__subsystem__);
|
||||
LONG(__idata_start)
|
||||
LONG(__idata_end)
|
||||
LONG(_main)
|
||||
|
||||
*(.init)
|
||||
*(.text)
|
||||
*(SORT(.text$*))
|
||||
*(.text.*)
|
||||
*(.glue_7t)
|
||||
*(.glue_7)
|
||||
___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
|
||||
LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*)); LONG (0);
|
||||
___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
|
||||
LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*)); LONG (0);
|
||||
*(.fini)
|
||||
/* ??? Why is .gcc_exc here? */
|
||||
*(.gcc_exc)
|
||||
PROVIDE (etext = .);
|
||||
*(.gcc_except_table)
|
||||
}
|
||||
|
||||
.rdata ALIGN(16) :
|
||||
{
|
||||
*(.rdata)
|
||||
*(SORT(.rdata$*))
|
||||
___RUNTIME_PSEUDO_RELOC_LIST__ = .;
|
||||
__RUNTIME_PSEUDO_RELOC_LIST__ = .;
|
||||
*(.rdata_runtime_pseudo_reloc)
|
||||
___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
|
||||
__RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
|
||||
}
|
||||
.CRT ALIGN(16) :
|
||||
{
|
||||
___crt_xc_start__ = . ;
|
||||
*(SORT(.CRT$XC*)) /* C initialization */
|
||||
___crt_xc_end__ = . ;
|
||||
___crt_xi_start__ = . ;
|
||||
*(SORT(.CRT$XI*)) /* C++ initialization */
|
||||
___crt_xi_end__ = . ;
|
||||
___crt_xl_start__ = . ;
|
||||
*(SORT(.CRT$XL*)) /* TLS callbacks */
|
||||
/* ___crt_xl_end__ is defined in the TLS Directory support code */
|
||||
___crt_xp_start__ = . ;
|
||||
*(SORT(.CRT$XP*)) /* Pre-termination */
|
||||
___crt_xp_end__ = . ;
|
||||
___crt_xt_start__ = . ;
|
||||
*(SORT(.CRT$XT*)) /* Termination */
|
||||
___crt_xt_end__ = . ;
|
||||
}
|
||||
|
||||
.data ALIGN(16) :
|
||||
{
|
||||
PROVIDE ( __data_start__ = .) ;
|
||||
*(.data)
|
||||
*(.data2)
|
||||
*(SORT(.data$*))
|
||||
*(.jcr)
|
||||
__CRT_MT = .;
|
||||
LONG(1);
|
||||
PROVIDE ( __data_end__ = .) ;
|
||||
*(.data_cygwin_nocopy)
|
||||
___iend = . ;
|
||||
}
|
||||
|
||||
.eh_frame BLOCK(16) :
|
||||
{
|
||||
PROVIDE (___EH_FRAME_BEGIN__ = .) ;
|
||||
*(.eh_frame*)
|
||||
___FRAME_END__ = . ;
|
||||
LONG(0);
|
||||
}
|
||||
|
||||
.idata ALIGN(16):
|
||||
{
|
||||
__idata_start = .;
|
||||
SORT(*)(.idata$2)
|
||||
SORT(*)(.idata$3)
|
||||
/* These zeroes mark the end of the import list. */
|
||||
LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
|
||||
SORT(*)(.idata$4)
|
||||
SORT(*)(.idata$5)
|
||||
SORT(*)(.idata$6)
|
||||
SORT(*)(.idata$7)
|
||||
__idata_end = . ;
|
||||
}
|
||||
|
||||
bss ALIGN(16):
|
||||
{
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
. = ALIGN(16);
|
||||
___cmdline = .;
|
||||
. = . + 256;
|
||||
___pgmname = .;
|
||||
. = . + 1024 + 16;
|
||||
___stacktop = .;
|
||||
___memsize = . ;
|
||||
}
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.debug$S)
|
||||
*(.debug$T)
|
||||
*(.debug$F)
|
||||
*(.drectve)
|
||||
*(.note.GNU-stack)
|
||||
*(.comment)
|
||||
/* *(.eh_frame) */
|
||||
*(.debug_abbrev)
|
||||
*(.debug_info)
|
||||
*(.debug_line)
|
||||
*(.debug_frame)
|
||||
*(.debug_loc)
|
||||
*(.debug_pubnames)
|
||||
*(.debug_aranges)
|
||||
*(.debug_ranges)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -305,7 +305,12 @@ int audio_thread(void *param)
|
||||
};
|
||||
active = 1;
|
||||
sync_audio(hBuff, buffsize);
|
||||
sound_state = PLAY;
|
||||
/* Do NOT clobber a stop/pause command that arrived (e.g. a
|
||||
* seek set PLAY_2_STOP) while we were restarting the buffer -
|
||||
* only advance the PREPARE/resume path to PLAY. Otherwise the
|
||||
* decoder's "wait for STOP" seek handshake would wait forever. */
|
||||
if(sound_state == PREPARE || sound_state == PAUSE_2_PLAY)
|
||||
sound_state = PLAY;
|
||||
|
||||
/* breaktrough */
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Полная сборка FPlay (медиаплеер KolibriOS).
|
||||
# Full build for the FPlay KolibriOS media player.
|
||||
#
|
||||
# Поднимает окружение kos32-тулчейна (PATH, fasm, путь к либам) и собирает
|
||||
# ВСЕ объекты заново + линкует + objcopy в MENUET-бинарь. Голый `make` этого
|
||||
# не делает: в Makefile нет -L и он ждёт тулчейн уже в PATH.
|
||||
#
|
||||
# Запуск из Git Bash / MSYS:
|
||||
# ./build.sh - полная сборка (all)
|
||||
# ./build.sh clean - удалить .o, бинарь, .map
|
||||
#
|
||||
# Пути можно переопределить переменными окружения, если тулчейн лежит иначе:
|
||||
# TOOLCHAIN_BIN=... LIB_DIR=... FASM=... ./build.sh
|
||||
#
|
||||
set -u
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Расположение инструментов (значения по умолчанию = как на этой машине)
|
||||
# ---------------------------------------------------------------------------
|
||||
TOOLCHAIN_BIN="${TOOLCHAIN_BIN:-/c/MinGW/msys/1.0/home/autobuild/tools/win32/bin}"
|
||||
LIB_DIR="${LIB_DIR:-/c/MinGW/msys/1.0/home/autobuild/tools/win32/lib}"
|
||||
FASM="${FASM:-/c/Kolibri/Infrastructure/C--/fasm.exe}"
|
||||
|
||||
# Каталог скрипта = корень исходников fplay; работаем относительно него.
|
||||
cd "$(dirname "$0")" || { echo "ERROR: cannot cd to script dir"; exit 1; }
|
||||
SDK_DIR="$(cd ../../sdk && pwd)" || { echo "ERROR: SDK dir (../../sdk) not found"; exit 1; }
|
||||
|
||||
export PATH="$TOOLCHAIN_BIN:$PATH"
|
||||
|
||||
CC=kos32-gcc
|
||||
LD=kos32-ld
|
||||
OBJCOPY=kos32-objcopy
|
||||
NAME=fplay
|
||||
|
||||
# fasm: явный путь, иначе из PATH
|
||||
if [ -x "$FASM" ]; then FASM_CMD="$FASM"; else FASM_CMD="fasm"; fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Флаги (идентичны Makefile проекта)
|
||||
# ---------------------------------------------------------------------------
|
||||
CFLAGS="-U_Win32 -U_WIN32 -U__MINGW32__ -c -O2 -fno-ident -std=c99 -fomit-frame-pointer -mno-ms-bitfields"
|
||||
DEFINES="-DDEBUG=1 -D_GNU_SOURCE -DHAVE_VAAPI -DPACKAGE_NAME=\"Fplay-vaapi\""
|
||||
INCLUDES="-I. -I$SDK_DIR/sources/newlib/libc/include -I$SDK_DIR/sources/ffmpeg/ffmpeg-2.8"
|
||||
INCLUDES="$INCLUDES -I$SDK_DIR/sources/freetype/include -I$SDK_DIR/sources/vaapi/libva-1.6.2"
|
||||
|
||||
LIBS="-lavdevice.dll -lavformat.dll -lavcodec.dll -lavutil.dll -lswscale.dll"
|
||||
LIBS="$LIBS -lswresample.dll -lsound -lpixlib3 -lfreetype.dll -lva.dll -lgcc -lc.dll"
|
||||
|
||||
LDFLAGS="-static --subsystem native --stack 0x200000 -Map fplay.map -Tapp-dynamic.lds --image-base 0"
|
||||
|
||||
# Исходники
|
||||
C_SOURCES="audio.c decoder.c fplay.c vaapi.c video.c utils.c \
|
||||
winlib/button.c winlib/caption.c winlib/fontlib.c \
|
||||
winlib/frame.c winlib/panel.c winlib/window.c"
|
||||
ASM_SOURCES="opendial.asm skin/skin.asm"
|
||||
|
||||
# Объекты в том порядке линковки, что использует проект
|
||||
OBJECTS="opendial.o audio.o decoder.o fplay.o vaapi.o video.o utils.o \
|
||||
skin/skin.o winlib/button.o winlib/caption.o winlib/fontlib.o \
|
||||
winlib/frame.o winlib/panel.o winlib/window.o"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# clean
|
||||
# ---------------------------------------------------------------------------
|
||||
if [ "${1:-}" = "clean" ]; then
|
||||
rm -f $OBJECTS "$NAME" fplay.map
|
||||
echo "cleaned: objects, $NAME, fplay.map"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Проверка инструментов
|
||||
# ---------------------------------------------------------------------------
|
||||
command -v "$CC" >/dev/null 2>&1 || { echo "ERROR: $CC not found (TOOLCHAIN_BIN=$TOOLCHAIN_BIN)"; exit 1; }
|
||||
command -v "$LD" >/dev/null 2>&1 || { echo "ERROR: $LD not found (TOOLCHAIN_BIN=$TOOLCHAIN_BIN)"; exit 1; }
|
||||
command -v "$OBJCOPY" >/dev/null 2>&1 || { echo "ERROR: $OBJCOPY not found (TOOLCHAIN_BIN=$TOOLCHAIN_BIN)"; exit 1; }
|
||||
command -v "$FASM_CMD" >/dev/null 2>&1 || { echo "ERROR: fasm not found (FASM=$FASM)"; exit 1; }
|
||||
[ -d "$LIB_DIR" ] || { echo "ERROR: LIB_DIR not found: $LIB_DIR"; exit 1; }
|
||||
|
||||
echo "SDK_DIR = $SDK_DIR"
|
||||
echo "toolchain = $TOOLCHAIN_BIN"
|
||||
echo
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Компиляция всех объектов заново (полная сборка)
|
||||
# ---------------------------------------------------------------------------
|
||||
fail=0
|
||||
|
||||
for src in $C_SOURCES; do
|
||||
obj="${src%.c}.o"
|
||||
printf ' CC %s\n' "$src"
|
||||
$CC $CFLAGS $DEFINES $INCLUDES -o "$obj" "$src" || { echo " ^^ FAILED: $src"; fail=1; }
|
||||
done
|
||||
|
||||
for src in $ASM_SOURCES; do
|
||||
obj="${src%.asm}.o"
|
||||
printf ' ASM %s\n' "$src"
|
||||
"$FASM_CMD" "$src" "$obj" || { echo " ^^ FAILED: $src"; fail=1; }
|
||||
done
|
||||
|
||||
if [ $fail -ne 0 ]; then
|
||||
echo
|
||||
echo "COMPILE FAILED - see errors above"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Линковка + objcopy в бинарь MENUET
|
||||
# ---------------------------------------------------------------------------
|
||||
echo
|
||||
printf ' LINK %s\n' "$NAME"
|
||||
$LD $LDFLAGS -L"$LIB_DIR" -o "$NAME" $OBJECTS $LIBS || { echo "LINK FAILED"; exit 1; }
|
||||
|
||||
printf ' BIN %s\n' "$NAME"
|
||||
$OBJCOPY "$NAME" -O binary || { echo "OBJCOPY FAILED"; exit 1; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Итог
|
||||
# ---------------------------------------------------------------------------
|
||||
size=$(stat -c %s "$NAME" 2>/dev/null || wc -c < "$NAME")
|
||||
hdr=$(head -c 6 "$NAME" 2>/dev/null)
|
||||
echo
|
||||
echo "BUILD OK"
|
||||
echo " file : $(pwd)/$NAME"
|
||||
echo " size : $size bytes"
|
||||
echo " header: $hdr"
|
||||
[ "$hdr" = "MENUET" ] || echo " WARNING: header is not MENUET - check app-dynamic.lds"
|
||||
@@ -14,6 +14,11 @@
|
||||
#include "sound.h"
|
||||
#include "fplay.h"
|
||||
|
||||
/* 0 = decode everything, 1 = skip non-reference frames. Driven by the A/V
|
||||
* sync loop in video.c (render_time) - the picture falling behind the audio
|
||||
* clock raises it, catching up clears it. */
|
||||
volatile int g_skip_level = 0;
|
||||
|
||||
static struct decoder ffmpeg_decoder;
|
||||
static struct decoder* init_ffmpeg_decoder(vst_t *vst);
|
||||
|
||||
@@ -107,28 +112,31 @@ static vframe_t *get_input_frame(vst_t *vst)
|
||||
|
||||
static void put_output_frame(vst_t *vst, vframe_t *vframe)
|
||||
{
|
||||
mutex_lock(&vst->output_lock);
|
||||
if(list_empty(&vst->output_list))
|
||||
list_add_tail(&vframe->list, &vst->output_list);
|
||||
else
|
||||
{
|
||||
vframe_t *cur;
|
||||
vframe_t *cur;
|
||||
int inserted = 0;
|
||||
|
||||
cur = list_first_entry(&vst->output_list,vframe_t,list);
|
||||
if(vframe->pts < cur->pts)
|
||||
list_add_tail(&vframe->list, &vst->output_list);
|
||||
else
|
||||
mutex_lock(&vst->output_lock);
|
||||
|
||||
/* Keep the list sorted by pts: walk from the tail, insert after the first
|
||||
* frame not newer than ours (>= keeps equal-pts frames in arrival order);
|
||||
* no match = ours is the earliest (or the list is empty) - new head.
|
||||
* The previous version added pre-head frames to the TAIL (unsorted) and,
|
||||
* worse, a frame whose pts equalled every queued one matched nothing and
|
||||
* was added to NO list at all - each such frame leaked one of the 4
|
||||
* decoder vframes for good, and four of them froze the video (happens
|
||||
* with broken/duplicate container timestamps). */
|
||||
list_for_each_entry_reverse(cur, &vst->output_list, list)
|
||||
{
|
||||
if(vframe->pts >= cur->pts)
|
||||
{
|
||||
list_for_each_entry_reverse(cur,&vst->output_list,list)
|
||||
{
|
||||
if(vframe->pts > cur->pts)
|
||||
{
|
||||
list_add(&vframe->list, &cur->list);
|
||||
break;
|
||||
};
|
||||
};
|
||||
list_add(&vframe->list, &cur->list);
|
||||
inserted = 1;
|
||||
break;
|
||||
};
|
||||
};
|
||||
if(!inserted)
|
||||
list_add(&vframe->list, &vst->output_list);
|
||||
|
||||
vst->frames_count++;
|
||||
mutex_unlock(&vst->output_lock);
|
||||
};
|
||||
@@ -150,6 +158,12 @@ int decode_video(vst_t* vst)
|
||||
if( get_packet(&vst->q_video, &pkt) == 0 )
|
||||
return 0;
|
||||
|
||||
/* Adaptive frame skipping: when the picture keeps falling behind the audio
|
||||
* clock (set from render_time in video.c), ask the codec to skip the
|
||||
* non-reference frames so decode gets lighter and the video keeps pace;
|
||||
* back to full quality once it has caught up. */
|
||||
vst->vCtx->skip_frame = g_skip_level ? AVDISCARD_NONREF : AVDISCARD_DEFAULT;
|
||||
|
||||
frameFinished = 0;
|
||||
|
||||
mutex_lock(&vst->gpu_lock);
|
||||
|
||||
+873
-385
File diff suppressed because it is too large.
Load diff
@@ -49,6 +49,7 @@ struct render
|
||||
uint32_t layout;
|
||||
bitmap_t *bitmap[4];
|
||||
bitmap_t *last_bitmap;
|
||||
vframe_t *last_frame; /* last decoded frame, kept for redraw on resize */
|
||||
|
||||
uint32_t ctx_format;
|
||||
int target;
|
||||
@@ -160,8 +161,11 @@ struct vstate
|
||||
struct decoder *decoder;
|
||||
int snd_format;
|
||||
volatile int frames_count;
|
||||
/* written by the reader thread (=1) and the audio thread (=0); keep it in
|
||||
* its own word (not a shared bitfield) so those writes don't race on a
|
||||
* read-modify-write of neighbouring flags */
|
||||
volatile int audio_timer_valid;
|
||||
unsigned int has_sound:1;
|
||||
unsigned int audio_timer_valid:1;
|
||||
unsigned int blit_bitmap:1; /* hardware RGBA blitter */
|
||||
unsigned int blit_texture:1; /* hardware RGBA blit and scale */
|
||||
unsigned int blit_planar:1; /* hardbare YUV blit and scale */
|
||||
@@ -171,6 +175,7 @@ struct vstate
|
||||
#define DECODER_THREAD 1
|
||||
#define AUDIO_THREAD 2
|
||||
#define VIDEO_THREAD 4
|
||||
#define READER_THREAD 8
|
||||
|
||||
extern volatile int threads_running;
|
||||
extern astream_t astream;
|
||||
@@ -189,6 +194,7 @@ int audio_thread(void *param);
|
||||
void set_audio_volume(int left, int right);
|
||||
|
||||
int video_thread(void *param);
|
||||
int read_thread(void *param);
|
||||
void flush_video(vst_t* vst);
|
||||
|
||||
int init_video_decoder(vst_t *vst);
|
||||
|
||||
@@ -137,9 +137,11 @@ dd filefilter.end - filefilter
|
||||
db 'mpg',0
|
||||
db 'mpeg',0
|
||||
db 'mkv',0
|
||||
db 'mp3',0
|
||||
db 'mp4',0
|
||||
db 'webm',0
|
||||
db 'wmv',0
|
||||
db 'vob',0
|
||||
.end:
|
||||
db 0
|
||||
|
||||
|
||||
+980
-47
File diff suppressed because it is too large.
Load diff
@@ -277,7 +277,22 @@ int draw_progress(progress_t *prg, int background)
|
||||
};
|
||||
};
|
||||
|
||||
len = prg->current*prg->ctrl.w/(prg->max - prg->min);
|
||||
/* max<=1 means the duration is unknown: keep the fill bar EMPTY rather
|
||||
* than showing a bogus full bar. Otherwise compute and CLAMP the fill in
|
||||
* float before the int conversion (with an unknown duration the unclamped
|
||||
* product would overflow len and run the fill loop past the pixmap). */
|
||||
if(prg->max <= 1.0f)
|
||||
len = 0;
|
||||
else
|
||||
{
|
||||
float flen = prg->current*prg->ctrl.w/(prg->max - prg->min);
|
||||
|
||||
if(flen < 0.0f)
|
||||
flen = 0.0f;
|
||||
if(flen > prg->ctrl.w)
|
||||
flen = prg->ctrl.w;
|
||||
len = (int)flen;
|
||||
}
|
||||
|
||||
src = res_prg_level;
|
||||
|
||||
@@ -473,6 +488,12 @@ int sld_proc(ctrl_t *ctrl, uint32_t msg, uint32_t arg1, uint32_t arg2)
|
||||
slider_t *sld = (slider_t*)ctrl;
|
||||
int pos;
|
||||
|
||||
/* the window is too narrow for the volume slider - the panel (parent)
|
||||
* spans the window, so its width is the window width; skip drawing AND
|
||||
* interaction (hover would otherwise flip the panel layout) */
|
||||
if(ctrl->parent->w < 320)
|
||||
return 0;
|
||||
|
||||
switch( msg )
|
||||
{
|
||||
case MSG_PAINT:
|
||||
|
||||
@@ -99,11 +99,13 @@ void update_caption_size(window_t *win)
|
||||
cpt->close_btn->ctrl.rc.r = cpt->close_btn->ctrl.rc.l +
|
||||
cpt->close_btn->ctrl.w;
|
||||
|
||||
cpt->minimize_btn->ctrl.rc.l = win->w - 27 - 18 - 5;
|
||||
/* Order (left -> right): minimize, fullscreen, close.
|
||||
* minimize is the leftmost of the three, fullscreen sits in the middle. */
|
||||
cpt->minimize_btn->ctrl.rc.l = win->w - 27 - 18 - 18 - 5 - 5;
|
||||
cpt->minimize_btn->ctrl.rc.r = cpt->minimize_btn->ctrl.rc.l +
|
||||
cpt->minimize_btn->ctrl.w;
|
||||
|
||||
cpt->full_btn->ctrl.rc.l = win->w - 27 - 18 -18 - 5 - 5;
|
||||
cpt->full_btn->ctrl.rc.l = win->w - 27 - 18 - 5;
|
||||
cpt->full_btn->ctrl.rc.r = cpt->full_btn->ctrl.rc.l +
|
||||
cpt->full_btn->ctrl.w;
|
||||
};
|
||||
|
||||
@@ -83,7 +83,11 @@ int draw_text_ext(void *pixmap, uint32_t pitch, FT_Face face, char *text, rect_t
|
||||
x = rc->l << 6;
|
||||
y = rc->b;
|
||||
|
||||
w = (rc->r - rc->l) << 6;
|
||||
/* right edge in 26.6 fixed point; x is an absolute position, so the
|
||||
* fit check below must compare against the edge, not the width -
|
||||
* otherwise any text placed at a large x (e.g. right-aligned) would
|
||||
* fail the very first check and nothing would be drawn */
|
||||
w = rc->r << 6;
|
||||
|
||||
while( ch = *text++ )
|
||||
{
|
||||
@@ -96,13 +100,15 @@ int draw_text_ext(void *pixmap, uint32_t pitch, FT_Face face, char *text, rect_t
|
||||
x += delta.x ;
|
||||
}
|
||||
|
||||
if( x + face->glyph->advance.x > w)
|
||||
break;
|
||||
|
||||
/* load FIRST, then check the fit with THIS glyph's advance - the old
|
||||
* order tested against the stale advance of the previous glyph */
|
||||
err = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
|
||||
if ( err )
|
||||
continue;
|
||||
|
||||
if( x + face->glyph->advance.x > w)
|
||||
break;
|
||||
|
||||
err = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );
|
||||
if ( err )
|
||||
continue;
|
||||
@@ -118,6 +124,37 @@ int draw_text_ext(void *pixmap, uint32_t pitch, FT_Face face, char *text, rect_t
|
||||
};
|
||||
|
||||
|
||||
int text_width_ext(FT_Face face, char *text)
|
||||
{
|
||||
FT_UInt glyph_index;
|
||||
FT_Bool use_kerning = FT_HAS_KERNING( face );
|
||||
FT_UInt previous = 0;
|
||||
int x = 0;
|
||||
char ch;
|
||||
|
||||
while( ch = *text++ )
|
||||
{
|
||||
glyph_index = FT_Get_Char_Index( face, ansi2utf32(ch) );
|
||||
|
||||
if( use_kerning && previous && glyph_index )
|
||||
{
|
||||
FT_Vector delta;
|
||||
FT_Get_Kerning( face, previous, glyph_index, FT_KERNING_DEFAULT, &delta );
|
||||
x += delta.x;
|
||||
}
|
||||
if( FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ) )
|
||||
continue;
|
||||
|
||||
x += face->glyph->advance.x;
|
||||
previous = glyph_index;
|
||||
}
|
||||
/* round UP: truncating loses the fractional part, the string then does
|
||||
* not quite fit into the measured width and the renderer's fit check
|
||||
* silently drops the last glyph ("3:20" -> "3:2") */
|
||||
return (x + 63) >> 6; /* 26.6 fixed point -> pixels */
|
||||
}
|
||||
|
||||
|
||||
int init_fontlib()
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -32,6 +32,7 @@ extern int res_stop_btn_pressed[];
|
||||
//extern int res_minimize_btn_pressed[];
|
||||
|
||||
void update_panel_size(window_t *win);
|
||||
static void render_time_text(panel_t *panel);
|
||||
|
||||
int panel_proc(ctrl_t *ctrl, uint32_t msg, uint32_t arg1, uint32_t arg2);
|
||||
|
||||
@@ -220,6 +221,10 @@ void draw_panel(panel_t *panel)
|
||||
send_message(child, MSG_PAINT, 0, 0);
|
||||
child = (ctrl_t*)child->link.next;
|
||||
};
|
||||
|
||||
/* a full panel repaint just erased the time text in the pixmap; put it
|
||||
* back so it does not blink out until the next second ticks over */
|
||||
render_time_text(panel);
|
||||
};
|
||||
|
||||
int panel_proc(ctrl_t *ctrl, uint32_t msg, uint32_t arg1, uint32_t arg2)
|
||||
@@ -261,6 +266,12 @@ int panel_proc(ctrl_t *ctrl, uint32_t msg, uint32_t arg1, uint32_t arg2)
|
||||
}
|
||||
break;
|
||||
|
||||
case MSG_WHEELUP:
|
||||
case MSG_WHEELDOWN:
|
||||
/* let the window handle the wheel (volume) wherever it is over
|
||||
* the panel, not only above a particular child control */
|
||||
return send_message(get_parent_window(ctrl), msg, arg1, arg2);
|
||||
|
||||
case MSG_COMMAND:
|
||||
switch((short)arg1)
|
||||
{
|
||||
@@ -293,3 +304,106 @@ void blit_panel(panel_t *panel)
|
||||
panel->ctrl.w, panel->ctrl.h, ctx->pixmap_pitch);
|
||||
};
|
||||
|
||||
extern int win_font;
|
||||
int draw_text_ext(void *pixmap, uint32_t pitch, int face, char *text, rect_t *rc, int color);
|
||||
int text_width_ext(int face, char *text);
|
||||
|
||||
/* elapsed / total time, bottom-right of the panel, in the same white as the
|
||||
* window-caption text. Rendered straight into the panel pixmap (like the
|
||||
* caption text) so it blits cleanly with no per-digit artefacts; the
|
||||
* panel-body gradient is restored under it first so old text is erased. */
|
||||
#define TIME_COLOR 0xFFFFFF /* white - same as the caption title text */
|
||||
#define TIME_MAXW 120
|
||||
#define TIME_Y0 25
|
||||
#define TIME_Y1 44
|
||||
#define TIME_BASE 40
|
||||
|
||||
static void fmt_time(char *buf, int sec)
|
||||
{
|
||||
int h, m, s;
|
||||
|
||||
if(sec < 0) /* clamp BEFORE splitting into h/m/s */
|
||||
sec = 0;
|
||||
|
||||
h = sec / 3600;
|
||||
m = (sec / 60) % 60;
|
||||
s = sec % 60;
|
||||
|
||||
if(h)
|
||||
sprintf(buf, "%d:%02d:%02d", h, m, s);
|
||||
else
|
||||
sprintf(buf, "%d:%02d", m, s);
|
||||
}
|
||||
|
||||
/* last shown values, so a full panel repaint can restore the text instead of
|
||||
* leaving a blank until the next second ticks over */
|
||||
static int time_elapsed = -1;
|
||||
static int time_total = 0;
|
||||
|
||||
/* render the cached time into the panel pixmap (no blit) */
|
||||
static void render_time_text(panel_t *panel)
|
||||
{
|
||||
ctx_t *ctx = &panel->ctx;
|
||||
int *pixmap = (int*)ctx->pixmap_data;
|
||||
int pitch4 = ctx->pixmap_pitch / 4;
|
||||
int panw = panel->ctrl.w;
|
||||
char buf[48], es[20], ts[20];
|
||||
rect_t rc;
|
||||
int x, y, x0, x1, tw;
|
||||
|
||||
if(time_elapsed < 0) /* nothing shown yet */
|
||||
return;
|
||||
if(panw < 260) /* too narrow: would clash with the left */
|
||||
return;
|
||||
|
||||
fmt_time(es, time_elapsed);
|
||||
if(time_total > 0)
|
||||
{
|
||||
fmt_time(ts, time_total);
|
||||
sprintf(buf, "%s / %s", es, ts);
|
||||
}
|
||||
else
|
||||
strcpy(buf, es); /* duration unknown: elapsed only */
|
||||
|
||||
x1 = panw - 12;
|
||||
x0 = x1 - TIME_MAXW;
|
||||
|
||||
/* restore the panel-body gradient under the text (one colour per row) */
|
||||
for(y = TIME_Y0; y < TIME_Y1; y++)
|
||||
{
|
||||
int c = res_panel_body[y];
|
||||
int *row = pixmap + y*pitch4;
|
||||
for(x = x0; x < x1; x++)
|
||||
row[x] = c;
|
||||
}
|
||||
|
||||
tw = text_width_ext(win_font, buf); /* right-align to x1 */
|
||||
if(tw > TIME_MAXW)
|
||||
tw = TIME_MAXW;
|
||||
|
||||
rc.l = x1 - tw;
|
||||
rc.t = TIME_Y0;
|
||||
rc.r = x1;
|
||||
rc.b = TIME_BASE;
|
||||
draw_text_ext(ctx->pixmap_data, ctx->pixmap_pitch, win_font, buf, &rc, TIME_COLOR);
|
||||
}
|
||||
|
||||
void draw_panel_time(panel_t *panel, int elapsed, int total)
|
||||
{
|
||||
ctx_t *ctx = &panel->ctx;
|
||||
int panw = panel->ctrl.w;
|
||||
int x0 = panw - 12 - TIME_MAXW;
|
||||
|
||||
time_elapsed = elapsed;
|
||||
time_total = total;
|
||||
|
||||
render_time_text(panel);
|
||||
|
||||
if(panw < 260)
|
||||
return;
|
||||
|
||||
Blit(ctx->pixmap_data, panel->draw.l + x0, panel->draw.t + TIME_Y0,
|
||||
x0, TIME_Y0, TIME_MAXW, TIME_Y1 - TIME_Y0,
|
||||
panw, PANEL_HEIGHT, ctx->pixmap_pitch);
|
||||
}
|
||||
|
||||
@@ -637,6 +637,18 @@ void update_rect(ctrl_t *ctrl)
|
||||
int ctx_w, ctx_h;
|
||||
int src_x, src_y;
|
||||
|
||||
/* In fullscreen the caption and panel are hidden, so no control should
|
||||
* blit itself onto the screen - otherwise e.g. the fullscreen button
|
||||
* repaints over the video after the switch. Walk up to the root window
|
||||
* and bail out while it is fullscreen. */
|
||||
{
|
||||
ctrl_t *root = ctrl;
|
||||
while(root->parent)
|
||||
root = root->parent;
|
||||
if(((window_t*)root)->win_state == FULLSCREEN)
|
||||
return;
|
||||
}
|
||||
|
||||
src_x = ctrl->rc.l - ctrl->ctx->offset_x;
|
||||
src_y = ctrl->rc.t - ctrl->ctx->offset_y;
|
||||
|
||||
|
||||
@@ -107,7 +107,9 @@ void init_winlib(void);
|
||||
|
||||
void draw_caption(caption_t *cpt);
|
||||
void draw_panel(panel_t *panel);
|
||||
void draw_panel_time(panel_t *panel, int elapsed, int total);
|
||||
void blit_caption(caption_t *cpt);
|
||||
void blit_panel(panel_t *panel);
|
||||
int init_caption(window_t *win);
|
||||
int init_panel(window_t *win);
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ EGL_OBJS = $(patsubst %.c, %.o, $(EGL_SRC))
|
||||
all: libegl.dll
|
||||
|
||||
libegl.dll: $(EGL_OBJS) Makefile
|
||||
$(LD) $(LDFLAGS) -o $@ $(MESA_SRC)/../egl.def $(EGL_OBJS) $(LIBS)
|
||||
$(file >egl.objs.rsp,$(EGL_OBJS))
|
||||
$(LD) $(LDFLAGS) -o $@ $(MESA_SRC)/../egl.def @egl.objs.rsp $(LIBS)
|
||||
# $(STRIP) $@
|
||||
# sed -e "s/ @[^ ]*//" egl1.def > egl.def
|
||||
mv -f $@ $(SDK_DIR)/bin
|
||||
@@ -65,4 +66,4 @@ libegl.dll: $(EGL_OBJS) Makefile
|
||||
$(CC) $(CFLAGS) $(MESA_DEFS) $(EGL_DEFS) $(INC_EGL) -o $@ $<
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -175,19 +175,23 @@ I965_OBJS = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(I965_SRC)))
|
||||
all: libgallium.a gbm_gallium_drm.dll
|
||||
|
||||
libgallium.a: $(GALLIUM_OBJS) Makefile
|
||||
$(AR) crs $@ $(GALLIUM_OBJS)
|
||||
$(file >gallium.objs.rsp,$(GALLIUM_OBJS))
|
||||
$(AR) crs $@ @gallium.objs.rsp
|
||||
mv -f $@ $(INSTALLDIR)
|
||||
|
||||
gbm_gallium_drm.dll: libgallium.a $(GBM_OBJS) Makefile
|
||||
$(LD) $(LDFLAGS) -o $@ $(GBM_OBJS) gbm.def $(LIBS)
|
||||
$(file >gbm.objs.rsp,$(GBM_OBJS))
|
||||
$(LD) $(LDFLAGS) -o $@ @gbm.objs.rsp gbm.def $(LIBS)
|
||||
mv -f $@ $(SDK_DIR)/bin
|
||||
|
||||
|
||||
i965_dri.drv: $(I965_OBJS) $(MESA_OBJS) dri.def Makefile
|
||||
$(LD) $(LDFLAGS) -o $@ $(I965_OBJS) $(MESA_OBJS) dri.def $(LIBS)
|
||||
$(file >i965.objs.rsp,$(I965_OBJS) $(MESA_OBJS))
|
||||
$(LD) $(LDFLAGS) -o $@ @i965.objs.rsp dri.def $(LIBS)
|
||||
mv -f $@ $(SDK_DIR)/bin
|
||||
|
||||
osmesa.dll: $(MESA_OBJS) $(OSMESA_OBJS) osmesa.def Makefile
|
||||
$(LD) $(LDFLAGS) --out-implib libosmesa.dll.a -o $@ $(MESA_OBJS) $(OSMESA_OBJS) osmesa.def $(LIBS)
|
||||
$(file >osmesa.objs.rsp,$(MESA_OBJS) $(OSMESA_OBJS))
|
||||
$(LD) $(LDFLAGS) --out-implib libosmesa.dll.a -o $@ @osmesa.objs.rsp osmesa.def $(LIBS)
|
||||
mv -f libosmesa.dll.a $(INSTALLDIR)
|
||||
mv -f $@ $(SDK_DIR)/bin
|
||||
|
||||
|
||||
@@ -113,7 +113,8 @@ GLSL_OBJS = $(patsubst %.cpp, %.o, $(patsubst %.c, %.o, $(GLSL_SRC)))
|
||||
all: libglsl.a
|
||||
|
||||
libglsl.a : $(GLSL_OBJS) Makefile
|
||||
$(AR) crs libglsl.a $(GLSL_OBJS)
|
||||
$(file >libglsl.objs.rsp,$(GLSL_OBJS))
|
||||
$(AR) crs libglsl.a @libglsl.objs.rsp
|
||||
mv -f libglsl.a $(INSTALLDIR)
|
||||
|
||||
%.o : %.c Makefile
|
||||
|
||||
@@ -37,7 +37,8 @@ LIBGL_OBJS = $(patsubst %.c, %.o, $(patsubst %.S, %.o, $(LIBGL_SRC)))
|
||||
all: libGL.dll
|
||||
|
||||
libGL.dll: $(LIBGL_OBJS) Makefile
|
||||
$(LD) $(LDFLAGS) -o $@ mesa.def $(LIBGL_OBJS) $(LIBS)
|
||||
$(file >libgl.objs.rsp,$(LIBGL_OBJS))
|
||||
$(LD) $(LDFLAGS) -o $@ mesa.def @libgl.objs.rsp $(LIBS)
|
||||
# $(STRIP) $@
|
||||
mv -f $@ $(SDK_DIR)/bin
|
||||
mv -f libGL.dll.a $(INSTALLDIR)
|
||||
@@ -48,4 +49,4 @@ libGL.dll: $(LIBGL_OBJS) Makefile
|
||||
%.o : %.S Makefile
|
||||
$(CC) -c -U_WIN32 -I $(MESA_SRC)/mesa -o $@ $<
|
||||
|
||||
|
||||
|
||||
@@ -424,17 +424,20 @@ I915_OBJS = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(I915_SRC)))
|
||||
all: osmesa.dll i965_dri.drv i915_dri.drv
|
||||
|
||||
i965_dri.drv: $(I965_OBJS) $(MESA_OBJS) dri.def Makefile
|
||||
$(LD) $(LDFLAGS) $(LIBPATH) -o $@ $(I965_OBJS) $(MESA_OBJS) dri.def $(LIBS)
|
||||
$(file >i965.objs.rsp,$(I965_OBJS) $(MESA_OBJS))
|
||||
$(LD) $(LDFLAGS) $(LIBPATH) -o $@ @i965.objs.rsp dri.def $(LIBS)
|
||||
# $(STRIP) $@
|
||||
mv -f $@ $(SDK_DIR)/bin
|
||||
|
||||
i915_dri.drv: $(I915_OBJS) $(MESA_OBJS) dri.def Makefile
|
||||
$(LD) $(LDFLAGS) $(LIBPATH) -o $@ $(I915_OBJS) $(MESA_OBJS) dri.def $(LIBS)
|
||||
$(file >i915.objs.rsp,$(I915_OBJS) $(MESA_OBJS))
|
||||
$(LD) $(LDFLAGS) $(LIBPATH) -o $@ @i915.objs.rsp dri.def $(LIBS)
|
||||
# $(STRIP) $@
|
||||
mv -f $@ $(SDK_DIR)/bin
|
||||
|
||||
osmesa.dll: $(MESA_OBJS) $(OSMESA_OBJS) osmesa.def Makefile
|
||||
$(LD) $(LDFLAGS) --out-implib libosmesa.dll.a -o $@ $(MESA_OBJS) $(OSMESA_OBJS) osmesa.def $(LIBS)
|
||||
$(file >osmesa.objs.rsp,$(MESA_OBJS) $(OSMESA_OBJS))
|
||||
$(LD) $(LDFLAGS) --out-implib libosmesa.dll.a -o $@ @osmesa.objs.rsp osmesa.def $(LIBS)
|
||||
mv -f libosmesa.dll.a $(INSTALLDIR)
|
||||
mv -f $@ $(SDK_DIR)/bin
|
||||
|
||||
|
||||
@@ -284,6 +284,7 @@ typedef enum {
|
||||
SDLK_MENU = 319,
|
||||
SDLK_POWER = 320, /* Power Macintosh power key */
|
||||
SDLK_EURO = 321, /* Some european keyboards */
|
||||
SDLK_UNDO = 322, /* Atari keyboard has Undo */
|
||||
|
||||
/* Add any other keys here */
|
||||
|
||||
|
||||
@@ -24,10 +24,29 @@
|
||||
*/
|
||||
|
||||
|
||||
/* An implementation of mutexes using semaphores */
|
||||
/*
|
||||
* KolibriOS mutex implementation.
|
||||
*
|
||||
* SDL here is built with -DDISABLE_THREADS, which would normally turn
|
||||
* SDL semaphores into stubs (returning NULL) and SDL mutexes into no-ops.
|
||||
* However, the KolibriOS audio backend (SDL_kolibri_audio.c) runs the SDL
|
||||
* audio callback in a REAL kernel thread created with _ksys_create_thread.
|
||||
* Applications such as ScummVM use SDL mutexes (via Common::Mutex) to guard
|
||||
* shared state (the audio mixer, software music drivers, ...) against
|
||||
* concurrent access from that audio thread and the main thread. With no-op
|
||||
* mutexes this synchronization silently disappears, leading to races and
|
||||
* crashes (e.g. a function pointer read while being overwritten -> a call
|
||||
* through a near-null pointer).
|
||||
*
|
||||
* So we provide a genuine, recursive mutex here, implemented as a small
|
||||
* spinlock that yields the CPU to other threads while waiting. It does not
|
||||
* rely on SDL semaphores (which stay stubbed out under DISABLE_THREADS).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/ksys.h>
|
||||
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_thread.h"
|
||||
@@ -35,28 +54,23 @@
|
||||
|
||||
|
||||
struct SDL_mutex {
|
||||
int recursive;
|
||||
Uint32 owner;
|
||||
SDL_sem *sem;
|
||||
volatile int guard; /* spinlock guarding the fields below */
|
||||
volatile uint32_t owner; /* tid of the owning thread, 0 if free */
|
||||
volatile int recursive; /* current lock/recursion depth */
|
||||
};
|
||||
|
||||
static uint32_t kos_current_tid(void)
|
||||
{
|
||||
ksys_thread_t ti;
|
||||
_ksys_thread_info(&ti, KSYS_THIS_SLOT);
|
||||
return ti.pid;
|
||||
}
|
||||
|
||||
/* Create a mutex */
|
||||
SDL_mutex *SDL_CreateMutex(void)
|
||||
{
|
||||
SDL_mutex *mutex;
|
||||
|
||||
/* Allocate mutex memory */
|
||||
mutex = (SDL_mutex *)malloc(sizeof(*mutex));
|
||||
if ( mutex ) {
|
||||
/* Create the mutex semaphore, with initial value 1 */
|
||||
mutex->sem = SDL_CreateSemaphore(1);
|
||||
mutex->recursive = 0;
|
||||
mutex->owner = 0;
|
||||
if ( ! mutex->sem ) {
|
||||
free(mutex);
|
||||
mutex = NULL;
|
||||
}
|
||||
} else {
|
||||
SDL_mutex *mutex = (SDL_mutex *)calloc(1, sizeof(*mutex));
|
||||
if ( ! mutex ) {
|
||||
SDL_OutOfMemory();
|
||||
}
|
||||
return mutex;
|
||||
@@ -66,71 +80,56 @@ SDL_mutex *SDL_CreateMutex(void)
|
||||
void SDL_DestroyMutex(SDL_mutex *mutex)
|
||||
{
|
||||
if ( mutex ) {
|
||||
if ( mutex->sem ) {
|
||||
SDL_DestroySemaphore(mutex->sem);
|
||||
}
|
||||
free(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lock the semaphore */
|
||||
/* Lock the mutex (recursive) */
|
||||
int SDL_mutexP(SDL_mutex *mutex)
|
||||
{
|
||||
#ifdef DISABLE_THREADS
|
||||
return 0;
|
||||
#else
|
||||
Uint32 this_thread;
|
||||
uint32_t me;
|
||||
|
||||
if ( mutex == NULL ) {
|
||||
SDL_SetError("Passed a NULL mutex");
|
||||
return -1;
|
||||
/* Some SDL subsystems (e.g. the event queue) never allocate a mutex
|
||||
under DISABLE_THREADS and pass NULL here. Those are only touched from
|
||||
the main thread, so treat a NULL mutex as a silent no-op instead of
|
||||
flooding errors. Real mutexes (the mixer's, ...) are non-NULL and are
|
||||
genuinely locked below. */
|
||||
if ( mutex == NULL )
|
||||
return 0;
|
||||
|
||||
me = kos_current_tid();
|
||||
for ( ;; ) {
|
||||
/* acquire the short internal guard */
|
||||
while ( __sync_lock_test_and_set(&mutex->guard, 1) )
|
||||
_ksys_thread_yield();
|
||||
|
||||
if ( mutex->owner == 0 || mutex->owner == me ) {
|
||||
mutex->owner = me;
|
||||
++mutex->recursive;
|
||||
__sync_lock_release(&mutex->guard);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* owned by another thread: drop the guard, yield and retry */
|
||||
__sync_lock_release(&mutex->guard);
|
||||
_ksys_thread_yield();
|
||||
}
|
||||
|
||||
this_thread = SDL_ThreadID();
|
||||
if ( mutex->owner == this_thread ) {
|
||||
++mutex->recursive;
|
||||
} else {
|
||||
/* The order of operations is important.
|
||||
We set the locking thread id after we obtain the lock
|
||||
so unlocks from other threads will fail.
|
||||
*/
|
||||
SDL_SemWait(mutex->sem);
|
||||
mutex->owner = this_thread;
|
||||
mutex->recursive = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif /* DISABLE_THREADS */
|
||||
}
|
||||
|
||||
/* Unlock the mutex */
|
||||
int SDL_mutexV(SDL_mutex *mutex)
|
||||
{
|
||||
#ifdef DISABLE_THREADS
|
||||
return 0;
|
||||
#else
|
||||
if ( mutex == NULL ) {
|
||||
SDL_SetError("Passed a NULL mutex");
|
||||
return -1;
|
||||
if ( mutex == NULL )
|
||||
return 0; /* no-op for NULL mutexes (see SDL_mutexP) */
|
||||
|
||||
while ( __sync_lock_test_and_set(&mutex->guard, 1) )
|
||||
_ksys_thread_yield();
|
||||
|
||||
if ( mutex->recursive > 0 ) {
|
||||
if ( --mutex->recursive == 0 )
|
||||
mutex->owner = 0;
|
||||
}
|
||||
|
||||
/* If we don't own the mutex, we can't unlock it */
|
||||
if ( SDL_ThreadID() != mutex->owner ) {
|
||||
SDL_SetError("mutex not owned by this thread");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( mutex->recursive ) {
|
||||
--mutex->recursive;
|
||||
} else {
|
||||
/* The order of operations is important.
|
||||
First reset the owner so another thread doesn't lock
|
||||
the mutex and set the ownership before we reset it,
|
||||
then release the lock semaphore.
|
||||
*/
|
||||
mutex->owner = 0;
|
||||
SDL_SemPost(mutex->sem);
|
||||
}
|
||||
__sync_lock_release(&mutex->guard);
|
||||
return 0;
|
||||
#endif /* DISABLE_THREADS */
|
||||
}
|
||||
@@ -190,16 +190,47 @@ void kos_PumpEvents(_THIS)
|
||||
static uint32_t old_mouse_but = 0;
|
||||
|
||||
mouse_pos = _ksys_get_mouse_pos(KSYS_MOUSE_WINDOW_POS);
|
||||
/* In fullscreen the game is centered, so translate the
|
||||
window-relative mouse position into game coordinates. */
|
||||
mouse_pos.x -= this->hidden->vx_ofs;
|
||||
mouse_pos.y -= this->hidden->vy_ofs;
|
||||
if (mouse_pos.x >= 0 && mouse_pos.x < this->hidden->win_size_x &&
|
||||
mouse_pos.y >= 0 && mouse_pos.y < this->hidden->win_size_y ||
|
||||
this->input_grab != SDL_GRAB_OFF) {
|
||||
this->input_grab == SDL_GRAB_ON) {
|
||||
|
||||
if (this->input_grab != SDL_GRAB_OFF) {
|
||||
center_pos.x = mouse_pos.x-this->hidden->win_size_x/2;
|
||||
center_pos.y = mouse_pos.y-this->hidden->win_size_y/2;
|
||||
if (center_pos.x || center_pos.y) {
|
||||
SDL_PrivateMouseMotion(0, 1, center_pos.x, center_pos.y);
|
||||
if (this->input_grab == SDL_GRAB_ON) {
|
||||
/* Relative deltas from the PREVIOUS cursor position,
|
||||
not from the window center: an absolute pointing
|
||||
device (QEMU usb-tablet, VirtualBox) snaps the
|
||||
cursor back after every warp, so center-based
|
||||
deltas grow with the distance and scale with fps.
|
||||
Re-center only near the border (keeps a real
|
||||
relative mouse turning); the first event after a
|
||||
warp only re-seeds prev_pos so the snap-back jump
|
||||
of a tablet never becomes a delta. */
|
||||
static ksys_pos_t prev_pos;
|
||||
static int prev_valid = 0;
|
||||
if (prev_valid) {
|
||||
int adx, ady;
|
||||
center_pos.x = mouse_pos.x - prev_pos.x;
|
||||
center_pos.y = mouse_pos.y - prev_pos.y;
|
||||
adx = center_pos.x < 0 ? -center_pos.x : center_pos.x;
|
||||
ady = center_pos.y < 0 ? -center_pos.y : center_pos.y;
|
||||
/* teleport-sized jump = warp echo, not motion */
|
||||
if (adx > this->hidden->win_size_x/2 ||
|
||||
ady > this->hidden->win_size_y/2)
|
||||
center_pos.x = center_pos.y = 0;
|
||||
if (center_pos.x || center_pos.y)
|
||||
SDL_PrivateMouseMotion(0, 1, center_pos.x, center_pos.y);
|
||||
}
|
||||
prev_pos = mouse_pos;
|
||||
prev_valid = 1;
|
||||
if (mouse_pos.x < this->hidden->win_size_x/8 ||
|
||||
mouse_pos.x >= this->hidden->win_size_x - this->hidden->win_size_x/8 ||
|
||||
mouse_pos.y < this->hidden->win_size_y/8 ||
|
||||
mouse_pos.y >= this->hidden->win_size_y - this->hidden->win_size_y/8) {
|
||||
kos_CheckMouseMode(this);
|
||||
prev_valid = 0;
|
||||
}
|
||||
} else {
|
||||
SDL_PrivateMouseMotion(0, 0, mouse_pos.x, mouse_pos.y);
|
||||
|
||||
@@ -13,38 +13,55 @@
|
||||
static SDL_VideoDevice * vm_suf=NULL;
|
||||
static int was_initialized = 0;
|
||||
static int scrn_size_defined = 0;
|
||||
static int is_fullscreen = 0;
|
||||
|
||||
static int has_null_cursor=0;
|
||||
static void* null_cursor;
|
||||
|
||||
#define WINDOW_BORDER_H 5
|
||||
#define WINDOW_BORDER_W 10
|
||||
#define WINDOW_BORDER_H 4
|
||||
#define WINDOW_BORDER_W 9
|
||||
|
||||
ksys_pos_t screen_size = {0};
|
||||
|
||||
void kos_SDL_RepaintWnd(void)
|
||||
{
|
||||
int win_pos_x, win_pos_y;
|
||||
int win_size_w = vm_suf->hidden->win_size_x+WINDOW_BORDER_W;
|
||||
int win_size_h = vm_suf->hidden->win_size_y+_ksys_get_skin_height()+WINDOW_BORDER_H;
|
||||
int win_pos_x = 0, win_pos_y = 0;
|
||||
int win_size_w, win_size_h;
|
||||
|
||||
if (!screen_size.val) {
|
||||
if (!screen_size.val)
|
||||
screen_size = _ksys_screen_size();
|
||||
win_pos_x = screen_size.x/2-win_size_w/2;
|
||||
win_pos_y = screen_size.y/2-win_size_h/2;
|
||||
|
||||
if (is_fullscreen) {
|
||||
win_pos_x = 0;
|
||||
win_pos_y = 0;
|
||||
win_size_w = screen_size.x + 1;
|
||||
win_size_h = screen_size.y + 1;
|
||||
} else {
|
||||
win_size_w = vm_suf->hidden->win_size_x + WINDOW_BORDER_W;
|
||||
win_size_h = vm_suf->hidden->win_size_y + _ksys_get_skin_height() + WINDOW_BORDER_H;
|
||||
win_pos_x = screen_size.x/2 - win_size_w/2;
|
||||
win_pos_y = screen_size.y/2 - win_size_h/2;
|
||||
}
|
||||
|
||||
_ksys_start_draw();
|
||||
if (vm_suf->hidden->fullscreen)
|
||||
// borderless window (style Y=1); the surface fills the screen
|
||||
_ksys_create_window(0, 0, vm_suf->hidden->win_size_x, vm_suf->hidden->win_size_y, vm_suf->hidden->__title, 0, 0x31);
|
||||
else
|
||||
// skinned window (Y=4), flag C=1: don't fill the work area
|
||||
if (is_fullscreen) {
|
||||
/* style 0x01: type I window (Y=1) "only define area, draw nothing" -
|
||||
no skin/caption. We paint a black background ourselves and blit
|
||||
the game centered (vx_ofs, vy_ofs). */
|
||||
_ksys_create_window(win_pos_x, win_pos_y, win_size_w, win_size_h, vm_suf->hidden->__title, 0, 0x01);
|
||||
_ksys_draw_bar(0, 0, win_size_w, win_size_h, 0);
|
||||
if (vm_suf && vm_suf->hidden->__video_buffer)
|
||||
_ksys_draw_bitmap(vm_suf->hidden->__video_buffer,
|
||||
vm_suf->hidden->vx_ofs, vm_suf->hidden->vy_ofs,
|
||||
vm_suf->hidden->win_size_x, vm_suf->hidden->win_size_y);
|
||||
} else {
|
||||
/* style 0x74: skinned fixed-size window (Y=4) with caption (A) and
|
||||
client-relative coords (B), plus bit C ("don't fill the working
|
||||
area") so the OS does not paint an opaque background. */
|
||||
_ksys_create_window(win_pos_x, win_pos_y, win_size_w, win_size_h, vm_suf->hidden->__title, 0, 0x74);
|
||||
|
||||
if (vm_suf && vm_suf->hidden->__video_buffer) {
|
||||
_ksys_draw_bitmap(vm_suf->hidden->__video_buffer, 0, 0,
|
||||
vm_suf->hidden->win_size_x, vm_suf->hidden->win_size_y);
|
||||
if (vm_suf && vm_suf->hidden->__video_buffer)
|
||||
_ksys_draw_bitmap(vm_suf->hidden->__video_buffer, 0, 0,
|
||||
vm_suf->hidden->win_size_x, vm_suf->hidden->win_size_y);
|
||||
}
|
||||
_ksys_end_draw();
|
||||
}
|
||||
@@ -66,8 +83,9 @@ static void kos_UnlockHWSurface(_THIS,SDL_Surface * surface) {/*STUB*/}
|
||||
static void kos_DirectUpdate(_THIS,int numrects,SDL_Rect * rects)
|
||||
{
|
||||
if (numrects) {
|
||||
_ksys_draw_bitmap(this->hidden->__video_buffer, 0,0,
|
||||
vm_suf->hidden->win_size_x,vm_suf->hidden->win_size_y);
|
||||
_ksys_draw_bitmap(this->hidden->__video_buffer,
|
||||
this->hidden->vx_ofs, this->hidden->vy_ofs,
|
||||
this->hidden->win_size_x, this->hidden->win_size_y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,14 +117,12 @@ SDL_Surface *kos_SetVideoMode(_THIS, SDL_Surface *current, int width, int height
|
||||
if (bpp!=24) return NULL;
|
||||
|
||||
current->flags=flags;
|
||||
this->hidden->fullscreen = (flags & SDL_FULLSCREEN) ? 1 : 0;
|
||||
current->w=width;
|
||||
current->h=height;
|
||||
current->pitch=width*(bpp>>3);
|
||||
|
||||
char info[128];
|
||||
sprintf(info, "SetVideoMode %dx%d bpp=%d fullscreen=%d flags=%08x\n",
|
||||
current->w, current->h, bpp, this->hidden->fullscreen, (unsigned)flags);
|
||||
char info[100];
|
||||
sprintf(info, "width = %d, height = %d, pitch = %d, bpp = %d\n", current->w, current->h, current->pitch, bpp);
|
||||
_ksys_debug_puts(info);
|
||||
|
||||
current->pixels=this->hidden->__video_buffer=realloc(this->hidden->__video_buffer, current->pitch*current->h);
|
||||
@@ -120,9 +136,21 @@ SDL_Surface *kos_SetVideoMode(_THIS, SDL_Surface *current, int width, int height
|
||||
this->hidden->win_size_y=height;
|
||||
vm_suf=this;
|
||||
|
||||
is_fullscreen = (flags & SDL_FULLSCREEN) ? 1 : 0;
|
||||
if (!screen_size.val)
|
||||
screen_size = _ksys_screen_size();
|
||||
if (is_fullscreen) {
|
||||
int sw = screen_size.x + 1, sh = screen_size.y + 1;
|
||||
this->hidden->vx_ofs = (sw > width) ? (sw - width)/2 : 0;
|
||||
this->hidden->vy_ofs = (sh > height) ? (sh - height)/2 : 0;
|
||||
} else {
|
||||
this->hidden->vx_ofs = 0;
|
||||
this->hidden->vy_ofs = 0;
|
||||
}
|
||||
|
||||
if (was_initialized) {
|
||||
if (this->hidden->fullscreen) {
|
||||
_ksys_change_window(0, 0, width, height);
|
||||
if (is_fullscreen) {
|
||||
_ksys_change_window(0, 0, screen_size.x + 1, screen_size.y + 1);
|
||||
} else {
|
||||
unsigned newheight = height+_ksys_get_skin_height()+WINDOW_BORDER_H;
|
||||
unsigned newwidth = width+WINDOW_BORDER_W;
|
||||
@@ -130,6 +158,7 @@ SDL_Surface *kos_SetVideoMode(_THIS, SDL_Surface *current, int width, int height
|
||||
int win_pos_y = screen_size.y/2-newheight/2;
|
||||
_ksys_change_window(win_pos_x, win_pos_y, newwidth, newheight);
|
||||
}
|
||||
kos_SDL_RepaintWnd();
|
||||
} else {
|
||||
_ksys_set_event_mask(0x27);
|
||||
was_initialized=1;
|
||||
@@ -173,7 +202,7 @@ static int kos_VideoInit(_THIS,SDL_PixelFormat * vformat)
|
||||
|
||||
static int kos_FlipHWSurface(_THIS,SDL_Surface * surface)
|
||||
{
|
||||
_ksys_draw_bitmap(surface->pixels, 0, 0, surface->w,surface->h);
|
||||
_ksys_draw_bitmap(surface->pixels, this->hidden->vx_ofs, this->hidden->vy_ofs, surface->w,surface->h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -227,7 +256,11 @@ void kos_FreeWMCursor(_THIS, WMcursor* cursor)
|
||||
|
||||
void kos_CheckMouseMode(_THIS)
|
||||
{
|
||||
if (this->input_grab == SDL_GRAB_OFF)
|
||||
/* Only recenter the pointer for an EXPLICIT grab (Ctrl+M mouselook).
|
||||
SDL forces input_grab = SDL_GRAB_FULLSCREEN in fullscreen, but ScummVM
|
||||
(and point-and-click games) need an absolute cursor there, so we do not
|
||||
recenter for that case. */
|
||||
if (this->input_grab != SDL_GRAB_ON)
|
||||
return;
|
||||
ksys_thread_t thread_info;
|
||||
int top = _ksys_thread_info(&thread_info, KSYS_THIS_SLOT);
|
||||
|
||||
@@ -57,7 +57,8 @@ $(SUBDIR)$(SLIBNAME): $(SUBDIR)$(SLIBNAME_WITH_MAJOR)
|
||||
|
||||
$(SUBDIR)$(SLIBNAME_WITH_MAJOR): $(OBJS) $(SLIBOBJS) $(SUBDIR)lib$(NAME).ver
|
||||
$(SLIB_CREATE_DEF_CMD)
|
||||
$$(LD) $(SHFLAGS) $(LDFLAGS) $(LDLIBFLAGS) $$(LD_O) $$(filter %.o,$$^) $(FFEXTRALIBS)
|
||||
$$(file >$(SUBDIR)link_objs.rsp,$$(filter %.o,$$^))
|
||||
$$(LD) $(SHFLAGS) $(LDFLAGS) $(LDLIBFLAGS) $$(LD_O) @$(SUBDIR)link_objs.rsp $(FFEXTRALIBS)
|
||||
$(SLIB_EXTRA_CMD)
|
||||
$(Q)cd ./$(SUBDIR) && $(LN_S) $(SLIBNAME_WITH_MAJOR) $(SLIBNAME)
|
||||
cd ..
|
||||
|
||||
@@ -0,0 +1,679 @@
|
||||
#ifndef __KOS_32_SYS_H__
|
||||
#define __KOS_32_SYS_H__
|
||||
|
||||
#include <newlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//#ifdef CONFIG_DEBUF
|
||||
// #define DBG(format,...) printf(format,##__VA_ARGS__)
|
||||
//#else
|
||||
// #define DBG(format,...)
|
||||
//#endif
|
||||
|
||||
#define TYPE_3_BORDER_WIDTH 5
|
||||
#define WIN_STATE_MINIMIZED 0x02
|
||||
#define WIN_STATE_ROLLED 0x04
|
||||
|
||||
typedef unsigned int color_t;
|
||||
|
||||
typedef union __attribute__((packed))
|
||||
{
|
||||
uint32_t val;
|
||||
struct
|
||||
{
|
||||
short x;
|
||||
short y;
|
||||
};
|
||||
}pos_t;
|
||||
|
||||
typedef union __attribute__((packed))
|
||||
{
|
||||
uint32_t val;
|
||||
struct
|
||||
{
|
||||
uint8_t state;
|
||||
uint8_t code;
|
||||
uint16_t ctrl_key;
|
||||
};
|
||||
}oskey_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned handle;
|
||||
unsigned io_code;
|
||||
void *input;
|
||||
int inp_size;
|
||||
void *output;
|
||||
int out_size;
|
||||
}ioctl_t;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct proc_info
|
||||
{
|
||||
unsigned long cpu_usage;
|
||||
unsigned short pos_in_stack;
|
||||
unsigned short slot;
|
||||
unsigned short reserved2;
|
||||
char name[12];
|
||||
unsigned long address;
|
||||
unsigned long memory_usage;
|
||||
unsigned long ID;
|
||||
unsigned long left,top;
|
||||
unsigned long width,height;
|
||||
unsigned short thread_state;
|
||||
unsigned short reserved3;
|
||||
unsigned long cleft, ctop, cwidth, cheight;
|
||||
unsigned char window_state;
|
||||
unsigned char reserved4[1024-71];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct kolibri_system_colors {
|
||||
color_t frame_area;
|
||||
color_t grab_bar;
|
||||
color_t grab_bar_button;
|
||||
color_t grab_button_text;
|
||||
color_t grab_text;
|
||||
color_t work_area;
|
||||
color_t work_button;
|
||||
color_t work_button_text;
|
||||
color_t work_text;
|
||||
color_t work_graph;
|
||||
};
|
||||
|
||||
static inline void begin_draw(void)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40" ::"a"(12),"b"(1));
|
||||
};
|
||||
|
||||
static inline
|
||||
void end_draw(void)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40" ::"a"(12),"b"(2));
|
||||
};
|
||||
|
||||
static inline void
|
||||
put_image(uint16_t x_coord, uint16_t y_coord,
|
||||
uint16_t size_x, uint16_t size_y, void *img)
|
||||
{
|
||||
__asm__ __volatile__("int $0x40"
|
||||
::"a"(25),
|
||||
"b"(img),
|
||||
"c"(size_x<<16 | size_y),
|
||||
"d"(x_coord<<16 | y_coord));
|
||||
};
|
||||
|
||||
|
||||
static inline
|
||||
void sys_create_window(int x, int y, int w, int h, const char *name,
|
||||
color_t workcolor, uint32_t style)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(0),
|
||||
"b"((x << 16) | ((w-1) & 0xFFFF)),
|
||||
"c"((y << 16) | ((h-1) & 0xFFFF)),
|
||||
"d"((style << 24) | (workcolor & 0xFFFFFF)),
|
||||
"D"(name),
|
||||
"S"(0) : "memory");
|
||||
};
|
||||
|
||||
#define OLD -1
|
||||
static inline
|
||||
void sys_change_window(int new_x, int new_y, int new_w, int new_h)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(67), "b"(new_x), "c"(new_y), "d"(new_w),"S"(new_h)
|
||||
);
|
||||
}
|
||||
|
||||
static inline
|
||||
void define_button(uint32_t x_w, uint32_t y_h, uint32_t id, uint32_t color)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(8),
|
||||
"b"(x_w),
|
||||
"c"(y_h),
|
||||
"d"(id),
|
||||
"S"(color));
|
||||
};
|
||||
|
||||
static inline
|
||||
void draw_line(int xs, int ys, int xe, int ye, color_t color)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(38), "d"(color),
|
||||
"b"((xs << 16) | xe),
|
||||
"c"((ys << 16) | ye));
|
||||
}
|
||||
|
||||
static inline
|
||||
void draw_bar(int x, int y, int w, int h, color_t color)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(13), "d"(color),
|
||||
"b"((x << 16) | w),
|
||||
"c"((y << 16) | h));
|
||||
}
|
||||
|
||||
static inline
|
||||
void draw_bitmap(void *bitmap, int x, int y, int w, int h)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(7), "b"(bitmap),
|
||||
"c"((w << 16) | h),
|
||||
"d"((x << 16) | y));
|
||||
}
|
||||
|
||||
static inline
|
||||
void draw_text_sys(const char *text, int x, int y, int len, color_t color)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(4),"d"(text),
|
||||
"b"((x << 16) | y),
|
||||
"S"(len),"c"(color)
|
||||
:"memory");
|
||||
}
|
||||
|
||||
/*
|
||||
void define_button_text(int x, int y, int w, int h, uint32_t id, uint32_t color, char* text)
|
||||
{
|
||||
define_button(x * 65536 + w, y * 65536 + h, id, color);
|
||||
|
||||
int tx = ((((-strlen(text))*8)+w)/2)+x;
|
||||
int ty = h/2-7+y;
|
||||
|
||||
draw_text_sys(text, tx, ty, strlen(text), 0x90000000);
|
||||
};
|
||||
*/
|
||||
|
||||
static inline
|
||||
uint32_t get_skin_height(void)
|
||||
{
|
||||
uint32_t height;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"int $0x40 \n\t"
|
||||
:"=a"(height)
|
||||
:"a"(48),"b"(4));
|
||||
return height;
|
||||
};
|
||||
|
||||
static inline void BeginDraw(void) __attribute__ ((alias ("begin_draw")));
|
||||
static inline void EndDraw(void) __attribute__ ((alias ("end_draw")));
|
||||
static inline void DrawWindow(int x, int y, int w, int h, const char *name,
|
||||
color_t workcolor, uint32_t style)
|
||||
__attribute__ ((alias ("sys_create_window")));
|
||||
static inline void DefineButton(void) __attribute__ ((alias ("define_button")));
|
||||
static inline void DrawLine(int xs, int ys, int xe, int ye, color_t color)
|
||||
__attribute__ ((alias ("draw_line")));
|
||||
static inline void DrawBar(int x, int y, int w, int h, color_t color)
|
||||
__attribute__ ((alias ("draw_bar")));
|
||||
static inline void DrawBitmap(void *bitmap, int x, int y, int w, int h)
|
||||
__attribute__ ((alias ("draw_bitmap")));
|
||||
static inline uint32_t GetSkinHeight(void) __attribute__ ((alias ("get_skin_height")));
|
||||
|
||||
|
||||
#define POS_SCREEN 0
|
||||
#define POS_WINDOW 1
|
||||
|
||||
static inline
|
||||
pos_t get_mouse_pos(int origin)
|
||||
{
|
||||
pos_t val;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"int $0x40 \n\t"
|
||||
"rol $16, %%eax"
|
||||
:"=a"(val)
|
||||
:"a"(37),"b"(origin));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline
|
||||
uint32_t get_mouse_buttons(void)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(37),"b"(2));
|
||||
return val;
|
||||
};
|
||||
|
||||
static inline
|
||||
uint32_t get_mouse_wheels(void)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"int $0x40 \n\t"
|
||||
:"=a"(val)
|
||||
:"a"(37),"b"(7));
|
||||
return val;
|
||||
};
|
||||
|
||||
static inline uint32_t load_cursor(void *path, uint32_t flags)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(37), "b"(4), "c"(path), "d"(flags));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint32_t set_cursor(uint32_t cursor)
|
||||
{
|
||||
uint32_t old;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(old)
|
||||
:"a"(37), "b"(5), "c"(cursor));
|
||||
return old;
|
||||
};
|
||||
|
||||
|
||||
#define EVM_REDRAW 1
|
||||
#define EVM_KEY 2
|
||||
#define EVM_BUTTON 4
|
||||
#define EVM_EXIT 8
|
||||
#define EVM_BACKGROUND 16
|
||||
#define EVM_MOUSE 32
|
||||
#define EVM_IPC 64
|
||||
#define EVM_STACK 128
|
||||
#define EVM_DEBUG 256
|
||||
#define EVM_STACK2 512
|
||||
#define EVM_MOUSE_FILTER 0x80000000
|
||||
#define EVM_CURSOR_FILTER 0x40000000
|
||||
|
||||
static inline uint32_t set_wanted_events_mask(uint32_t event_mask)
|
||||
{
|
||||
uint32_t old_event_mask;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(old_event_mask)
|
||||
:"a"(40),"b"(event_mask));
|
||||
|
||||
return old_event_mask;
|
||||
};
|
||||
|
||||
static inline int destroy_cursor(uint32_t cursor)
|
||||
{
|
||||
int ret;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(ret)
|
||||
:"a"(37), "b"(6), "c"(cursor)
|
||||
:"memory");
|
||||
return ret;
|
||||
};
|
||||
|
||||
static inline pos_t GetMousePos(int origin) __attribute__ ((alias ("get_mouse_pos")));
|
||||
static inline uint32_t GetMouseButtons(void) __attribute__ ((alias ("get_mouse_buttons")));
|
||||
static inline uint32_t GetMouseWheels(void) __attribute__ ((alias ("get_mouse_wheels")));
|
||||
|
||||
static inline uint32_t LoadCursor(void *path, uint32_t flags) __attribute__ ((alias ("load_cursor")));
|
||||
static inline uint32_t SetCursor(uint32_t cursor) __attribute__ ((alias ("set_cursor")));
|
||||
static inline int DestroyCursor(uint32_t cursor) __attribute__ ((alias ("destroy_cursor")));
|
||||
|
||||
|
||||
static inline
|
||||
uint32_t wait_for_event(uint32_t time)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(23), "b"(time));
|
||||
return val;
|
||||
};
|
||||
|
||||
static inline uint32_t check_os_event(void)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(11));
|
||||
return val;
|
||||
};
|
||||
|
||||
static inline uint32_t get_os_event(void)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(10));
|
||||
return val;
|
||||
};
|
||||
static inline uint32_t GetOsEvent(void) __attribute__ ((alias ("get_os_event")));
|
||||
|
||||
static inline
|
||||
uint32_t get_tick_count(void)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(26),"b"(9));
|
||||
return val;
|
||||
};
|
||||
|
||||
static inline
|
||||
uint64_t get_ns_count(void)
|
||||
{
|
||||
uint64_t val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=A"(val)
|
||||
:"a"(26), "b"(10));
|
||||
return val;
|
||||
};
|
||||
|
||||
static inline
|
||||
oskey_t get_key(void)
|
||||
{
|
||||
oskey_t val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(2));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline
|
||||
uint32_t get_os_button(void)
|
||||
{
|
||||
uint32_t val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(17));
|
||||
return val>>8;
|
||||
};
|
||||
|
||||
static inline uint32_t
|
||||
heap_init(void)
|
||||
{
|
||||
uint32_t heapsize;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(heapsize)
|
||||
:"a"(68),"b"(11)
|
||||
);
|
||||
|
||||
return heapsize;
|
||||
}
|
||||
|
||||
static inline uint32_t get_service(char *name)
|
||||
{
|
||||
uint32_t retval = 0;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(retval)
|
||||
:"a"(68),"b"(16),"c"(name)
|
||||
:"memory");
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
static inline int call_service(ioctl_t *io)
|
||||
{
|
||||
int retval;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(retval)
|
||||
:"a"(68),"b"(17),"c"(io)
|
||||
:"memory","cc");
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
|
||||
static inline void yield(void)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(68), "b"(1));
|
||||
};
|
||||
|
||||
static inline void delay(uint32_t time)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(5), "b"(time)
|
||||
:"memory");
|
||||
};
|
||||
|
||||
static inline
|
||||
void *user_alloc(size_t size)
|
||||
{
|
||||
void *val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(68),"b"(12),"c"(size));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline
|
||||
int user_free(void *mem)
|
||||
{
|
||||
int val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(68),"b"(13),"c"(mem));
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline
|
||||
void* user_realloc(void *mem, size_t size)
|
||||
{
|
||||
void *val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(68),"b"(20),"c"(size),"d"(mem)
|
||||
:"memory");
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
static inline
|
||||
int *user_unmap(void *base, size_t offset, size_t size)
|
||||
{
|
||||
int *val;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(val)
|
||||
:"a"(68),"b"(26),"c"(base),"d"(offset),"S"(size));
|
||||
return val;
|
||||
};
|
||||
|
||||
static inline void *UserAlloc(size_t size) __attribute__ ((alias ("user_alloc")));
|
||||
static inline int UserFree(void *mem) __attribute__ ((alias ("user_free")));
|
||||
static inline void* UserRealloc(void *mem, size_t size) __attribute__ ((alias ("user_realloc")));
|
||||
static inline int *UserUnmap(void *base, size_t offset, size_t size) __attribute__ ((alias ("user_unmap")));
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
unsigned long long raw;
|
||||
}ufile_t;
|
||||
|
||||
static inline ufile_t load_file(const char *path)
|
||||
{
|
||||
ufile_t uf;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"int $0x40"
|
||||
:"=A"(uf.raw)
|
||||
:"a" (68), "b"(27),"c"(path));
|
||||
|
||||
return uf;
|
||||
};
|
||||
static inline ufile_t LoadFile(const char *path) __attribute__ ((alias ("load_file")));
|
||||
|
||||
static inline int GetScreenSize(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(retval)
|
||||
:"a"(61), "b"(1));
|
||||
return retval;
|
||||
}
|
||||
|
||||
static inline
|
||||
pos_t max_screen_size()
|
||||
{
|
||||
pos_t size;
|
||||
pos_t size_tmp;
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:"=a"(size_tmp)
|
||||
:"a"(14));
|
||||
size.x = size_tmp.y;
|
||||
size.y = size_tmp.x;
|
||||
return size;
|
||||
};
|
||||
|
||||
static inline void get_system_colors(struct kolibri_system_colors *color_table)
|
||||
{
|
||||
__asm__ volatile ("int $0x40"
|
||||
:
|
||||
:"a"(48),"b"(3),"c"(color_table),"d"(40)
|
||||
);
|
||||
}
|
||||
|
||||
static inline void get_proc_info(char *info)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
:
|
||||
:"a"(9), "b"(info), "c"(-1)
|
||||
:"memory");
|
||||
};
|
||||
static inline void GetProcInfo(char *info) __attribute__ ((alias ("get_proc_info")));
|
||||
|
||||
|
||||
struct blit_call
|
||||
{
|
||||
int dstx;
|
||||
int dsty;
|
||||
int w;
|
||||
int h;
|
||||
|
||||
int srcx;
|
||||
int srcy;
|
||||
int srcw;
|
||||
int srch;
|
||||
|
||||
void *bitmap;
|
||||
int stride;
|
||||
};
|
||||
|
||||
static inline void Blit(void *bitmap, int dst_x, int dst_y,
|
||||
int src_x, int src_y, int w, int h,
|
||||
int src_w, int src_h, int stride)
|
||||
{
|
||||
volatile struct blit_call bc;
|
||||
|
||||
bc.dstx = dst_x;
|
||||
bc.dsty = dst_y;
|
||||
bc.w = w;
|
||||
bc.h = h;
|
||||
bc.srcx = src_x;
|
||||
bc.srcy = src_y;
|
||||
bc.srcw = src_w;
|
||||
bc.srch = src_h;
|
||||
bc.stride = stride;
|
||||
bc.bitmap = bitmap;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"int $0x40"
|
||||
::"a"(73),"b"(0),"c"(&bc.dstx));
|
||||
};
|
||||
|
||||
#define TLS_KEY_PID 0
|
||||
#define TLS_KEY_TID 4
|
||||
#define TLS_KEY_LOW_STACK 8
|
||||
#define TLS_KEY_HIGH_STACK 12
|
||||
#define TLS_KEY_LIBC 16
|
||||
|
||||
unsigned int tls_alloc(void);
|
||||
int tls_free(unsigned int key);
|
||||
|
||||
static inline int tls_set(unsigned int key, void *val)
|
||||
{
|
||||
int ret = -1;
|
||||
if(key < 4096)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"movl %0, %%fs:(%1)"
|
||||
::"r"(val),"r"(key));
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
static inline void *tls_get(unsigned int key)
|
||||
{
|
||||
void *val = (void*)-1;
|
||||
if(key < 4096)
|
||||
{
|
||||
__asm__ __volatile__(
|
||||
"movl %%fs:(%1), %0"
|
||||
:"=r"(val)
|
||||
:"r"(key));
|
||||
};
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
int create_thread(int (*proc)(void *param), void *param, int stack_size);
|
||||
|
||||
void* load_library(const char *name);
|
||||
|
||||
void* get_proc_address(void *handle, const char *proc_name);
|
||||
|
||||
void enumerate_libraries(int (*callback)(void *handle, const char* name,
|
||||
uint32_t base, uint32_t size, void *user_data),
|
||||
void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -213,24 +213,38 @@ static struct pix_driver sw_driver =
|
||||
};
|
||||
|
||||
|
||||
static struct pix_driver *hw_driver; /* cached GL/EGL driver */
|
||||
static int hw_state; /* 0 = untried, 1 = ok, 2 = failed */
|
||||
|
||||
uint32_t pxInit(int hw)
|
||||
{
|
||||
void *lib;
|
||||
struct pix_driver *(*drventry)(uint32_t service);
|
||||
struct pix_driver *drv;
|
||||
|
||||
uint32_t api_version;
|
||||
ioctl_t io;
|
||||
|
||||
if(driver != NULL)
|
||||
return driver->driver_caps;
|
||||
|
||||
driver = &sw_driver;
|
||||
|
||||
if(hw == 0)
|
||||
if(hw == 0) /* caller forces software */
|
||||
{
|
||||
driver = &sw_driver;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fd != 0)
|
||||
return driver->driver_caps;
|
||||
if(hw_state == 1) /* hw already initialized */
|
||||
{
|
||||
driver = hw_driver;
|
||||
return hw_driver->driver_caps;
|
||||
}
|
||||
|
||||
if(hw_state == 2) /* hw probed before and failed */
|
||||
{
|
||||
driver = &sw_driver;
|
||||
return 0;
|
||||
}
|
||||
|
||||
hw_state = 2; /* pessimistic until we succeed */
|
||||
driver = &sw_driver;
|
||||
|
||||
fd = get_service("DISPLAY");
|
||||
if (fd == 0)
|
||||
@@ -259,12 +273,13 @@ uint32_t pxInit(int hw)
|
||||
if( drventry == NULL)
|
||||
goto fail;
|
||||
|
||||
driver = drventry(fd);
|
||||
if(driver == NULL)
|
||||
{
|
||||
driver = &sw_driver;
|
||||
drv = drventry(fd);
|
||||
if(drv == NULL)
|
||||
goto fail;
|
||||
};
|
||||
|
||||
hw_driver = drv;
|
||||
hw_state = 1;
|
||||
driver = hw_driver;
|
||||
|
||||
if (driver->driver_caps)
|
||||
printf("2D caps %s%s%s\n",
|
||||
@@ -283,7 +298,10 @@ fail:
|
||||
|
||||
void pxFini()
|
||||
{
|
||||
if (driver->fini)
|
||||
driver->fini();
|
||||
if (hw_driver && hw_driver->fini)
|
||||
hw_driver->fini();
|
||||
hw_driver = NULL;
|
||||
hw_state = 0;
|
||||
driver = &sw_driver;
|
||||
};
|
||||
|
||||
@@ -57,7 +57,6 @@ err_0:
|
||||
static int update_fb(struct render *px, int name, int pitch)
|
||||
{
|
||||
GLenum status;
|
||||
EGLImageKHR screen;
|
||||
|
||||
EGLint attribs[] = {
|
||||
EGL_WIDTH, px->scr_width,
|
||||
@@ -77,8 +76,6 @@ static int update_fb(struct render *px, int name, int pitch)
|
||||
if(px->screen == EGL_NO_IMAGE_KHR)
|
||||
goto err_0;
|
||||
|
||||
px->screen = screen;
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, px->texture[TEX_SCREEN],0);
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user