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:
LeencyandBurer authored and Burer committed 2026-07-19 06:32:00 +00:00
1 parent bb58df2d87
commit 8c48f1d18b
29 files changed
+3265 -593

No files matched your search

+138
View File
@@ -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)
}
}
+6 -1
View File
@@ -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 */
+129
View File
@@ -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"
+32 -18
View File
@@ -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);
File diff suppressed because it is too large. Load diff
+7 -1
View File
@@ -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);
+2
View File
@@ -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
File diff suppressed because it is too large. Load diff
+22 -1
View File
@@ -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:
+4 -2
View File
@@ -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;
};
+41 -4
View File
@@ -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;
+114
View File
@@ -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);
}
+12
View File
@@ -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;
+2
View File
@@ -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
+33 -15
View File
@@ -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;
};
-3
View File
@@ -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.