summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio/player.c29
-rw-r--r--src/audio/player.h3
-rw-r--r--src/gmdocument.c2
-rw-r--r--src/media.c8
-rw-r--r--src/ui/documentwidget.c164
5 files changed, 188 insertions, 18 deletions
diff --git a/src/audio/player.c b/src/audio/player.c
index 177613bb..2d6767ea 100644
--- a/src/audio/player.c
+++ b/src/audio/player.c
@@ -146,6 +146,7 @@ iDeclareType(ContentSpec)
146struct Impl_ContentSpec { 146struct Impl_ContentSpec {
147 SDL_AudioFormat inputFormat; 147 SDL_AudioFormat inputFormat;
148 SDL_AudioSpec output; 148 SDL_AudioSpec output;
149 uint64_t totalSamples;
149 iRanges wavData; 150 iRanges wavData;
150}; 151};
151 152
@@ -168,6 +169,8 @@ struct Impl_Decoder {
168 size_t inputPos; 169 size_t inputPos;
169 iSampleBuf output; 170 iSampleBuf output;
170 iMutex outputMutex; 171 iMutex outputMutex;
172 uint64_t currentSample;
173 uint64_t totalSamples; /* zero if unknown */
171 iRanges wavData; 174 iRanges wavData;
172}; 175};
173 176
@@ -252,6 +255,7 @@ static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRang
252 } 255 }
253 } 256 }
254 iGuardMutex(&d->outputMutex, write_SampleBuf(&d->output, samples, n)); 257 iGuardMutex(&d->outputMutex, write_SampleBuf(&d->output, samples, n));
258 d->currentSample += n;
255 free(samples); 259 free(samples);
256 return ok_DecoderParseStatus; 260 return ok_DecoderParseStatus;
257} 261}
@@ -305,6 +309,8 @@ void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) {
305 spec->output.format, 309 spec->output.format,
306 spec->output.channels, 310 spec->output.channels,
307 spec->output.samples * 2); 311 spec->output.samples * 2);
312 d->currentSample = 0;
313 d->totalSamples = spec->totalSamples;
308 init_Mutex(&d->outputMutex); 314 init_Mutex(&d->outputMutex);
309 d->thread = new_Thread(run_Decoder_); 315 d->thread = new_Thread(run_Decoder_);
310 setUserData_Thread(d->thread, d); 316 setUserData_Thread(d->thread, d);
@@ -366,6 +372,7 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {
366 return content; 372 return content;
367 } 373 }
368 /* Read all the chunks. */ 374 /* Read all the chunks. */
375 int16_t blockAlign = 0;
369 while (!atEnd_Buffer(buf)) { 376 while (!atEnd_Buffer(buf)) {
370 readData_Buffer(buf, 4, magic); 377 readData_Buffer(buf, 4, magic);
371 const size_t size = read32_Stream(is); 378 const size_t size = read32_Stream(is);
@@ -381,7 +388,7 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {
381 const int16_t numChannels = read16_Stream(is); 388 const int16_t numChannels = read16_Stream(is);
382 const int32_t freq = read32_Stream(is); 389 const int32_t freq = read32_Stream(is);
383 const uint32_t bytesPerSecond = readU32_Stream(is); 390 const uint32_t bytesPerSecond = readU32_Stream(is);
384 const int16_t blockAlign = read16_Stream(is); 391 blockAlign = read16_Stream(is);
385 const int16_t bitsPerSample = read16_Stream(is); 392 const int16_t bitsPerSample = read16_Stream(is);
386 const uint16_t extSize = (size == 18 ? readU16_Stream(is) : 0); 393 const uint16_t extSize = (size == 18 ? readU16_Stream(is) : 0);
387 iUnused(bytesPerSecond); 394 iUnused(bytesPerSecond);
@@ -418,7 +425,8 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {
418 } 425 }
419 } 426 }
420 else if (memcmp(magic, "data", 4) == 0) { 427 else if (memcmp(magic, "data", 4) == 0) {
421 content.wavData = (iRanges){ pos_Stream(is), pos_Stream(is) + size }; 428 content.wavData = (iRanges){ pos_Stream(is), pos_Stream(is) + size };
429 content.totalSamples = (uint64_t) size_Range(&content.wavData) / blockAlign;
422 break; 430 break;
423 } 431 }
424 else { 432 else {
@@ -465,11 +473,16 @@ iBool isStarted_Player(const iPlayer *d) {
465 return d->device != 0; 473 return d->device != 0;
466} 474}
467 475
468void setFormatHint_Player(iPlayer *d, const char *hint) { 476iBool isPaused_Player(const iPlayer *d) {
477 if (!d->device) return iTrue;
478 return SDL_GetAudioDeviceStatus(d->device) == SDL_AUDIO_PAUSED;
479}
469 480
481void setFormatHint_Player(iPlayer *d, const char *hint) {
470} 482}
471 483
472void updateSourceData_Player(iPlayer *d, const iBlock *data, enum iPlayerUpdate update) { 484void updateSourceData_Player(iPlayer *d, const iBlock *data, enum iPlayerUpdate update) {
485 /* TODO: Add MIME as argument */
473 iInputBuf *input = d->data; 486 iInputBuf *input = d->data;
474 lock_Mutex(&input->mtx); 487 lock_Mutex(&input->mtx);
475 switch (update) { 488 switch (update) {
@@ -527,3 +540,13 @@ void stop_Player(iPlayer *d) {
527 d->decoder = NULL; 540 d->decoder = NULL;
528 } 541 }
529} 542}
543
544float time_Player(const iPlayer *d) {
545 if (!d->decoder) return 0;
546 return (float) ((double) d->decoder->currentSample / (double) d->spec.freq);
547}
548
549float duration_Player(const iPlayer *d) {
550 if (!d->decoder) return 0;
551 return (float) ((double) d->decoder->totalSamples / (double) d->spec.freq);
552}
diff --git a/src/audio/player.h b/src/audio/player.h
index 2138c556..5c17ef6c 100644
--- a/src/audio/player.h
+++ b/src/audio/player.h
@@ -41,3 +41,6 @@ void setPaused_Player (iPlayer *, iBool isPaused);
41void stop_Player (iPlayer *); 41void stop_Player (iPlayer *);
42 42
43iBool isStarted_Player (const iPlayer *); 43iBool isStarted_Player (const iPlayer *);
44iBool isPaused_Player (const iPlayer *);
45float time_Player (const iPlayer *);
46float duration_Player (const iPlayer *);
diff --git a/src/gmdocument.c b/src/gmdocument.c
index ff0e019b..e2696085 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -570,7 +570,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
570 pos.y += margin; 570 pos.y += margin;
571 run.bounds.pos = pos; 571 run.bounds.pos = pos;
572 run.bounds.size.x = d->size.x; 572 run.bounds.size.x = d->size.x;
573 run.bounds.size.y = 2 * lineHeight_Text(uiContent_FontId); 573 run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI;
574 run.visBounds = run.bounds; 574 run.visBounds = run.bounds;
575 run.text = iNullRange; 575 run.text = iNullRange;
576 run.color = 0; 576 run.color = 0;
diff --git a/src/media.c b/src/media.c
index b72ec32c..ddf5d45f 100644
--- a/src/media.c
+++ b/src/media.c
@@ -266,3 +266,11 @@ iBool audioInfo_Media(const iMedia *d, iMediaId audioId, iGmAudioInfo *info_out)
266 iZap(*info_out); 266 iZap(*info_out);
267 return iFalse; 267 return iFalse;
268} 268}
269
270iPlayer *audioPlayer_Media(const iMedia *d, iMediaId audioId) {
271 if (audioId > 0 && audioId <= size_PtrArray(&d->audio)) {
272 const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1);
273 return audio->player;
274 }
275 return NULL;
276}
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 165bf9cf..27a26d99 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -21,20 +21,22 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22 22
23#include "documentwidget.h" 23#include "documentwidget.h"
24#include "scrollwidget.h" 24
25#include "inputwidget.h"
26#include "labelwidget.h"
27#include "visbuf.h"
28#include "paint.h"
29#include "command.h"
30#include "keys.h"
31#include "util.h"
32#include "history.h"
33#include "app.h" 25#include "app.h"
26#include "audio/player.h"
27#include "command.h"
34#include "gmdocument.h" 28#include "gmdocument.h"
35#include "gmrequest.h" 29#include "gmrequest.h"
36#include "gmutil.h" 30#include "gmutil.h"
31#include "history.h"
32#include "inputwidget.h"
33#include "keys.h"
34#include "labelwidget.h"
37#include "media.h" 35#include "media.h"
36#include "paint.h"
37#include "scrollwidget.h"
38#include "util.h"
39#include "visbuf.h"
38 40
39#include <the_Foundation/file.h> 41#include <the_Foundation/file.h>
40#include <the_Foundation/fileinfo.h> 42#include <the_Foundation/fileinfo.h>
@@ -170,6 +172,7 @@ struct Impl_DocumentWidget {
170 iRangecc foundMark; 172 iRangecc foundMark;
171 int pageMargin; 173 int pageMargin;
172 iPtrArray visibleLinks; 174 iPtrArray visibleLinks;
175 iPtrArray visiblePlayers; /* currently playing audio */
173 const iGmRun * hoverLink; 176 const iGmRun * hoverLink;
174 const iGmRun * contextLink; 177 const iGmRun * contextLink;
175 iBool noHoverWhileScrolling; 178 iBool noHoverWhileScrolling;
@@ -233,6 +236,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
233 init_String(&d->sourceMime); 236 init_String(&d->sourceMime);
234 init_Block(&d->sourceContent, 0); 237 init_Block(&d->sourceContent, 0);
235 init_PtrArray(&d->visibleLinks); 238 init_PtrArray(&d->visibleLinks);
239 init_PtrArray(&d->visiblePlayers);
236 init_Click(&d->click, d, SDL_BUTTON_LEFT); 240 init_Click(&d->click, d, SDL_BUTTON_LEFT);
237 addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); 241 addChild_Widget(w, iClob(d->scroll = new_ScrollWidget()));
238 d->menu = NULL; /* created when clicking */ 242 d->menu = NULL; /* created when clicking */
@@ -253,6 +257,7 @@ void deinit_DocumentWidget(iDocumentWidget *d) {
253 deinit_Block(&d->sourceContent); 257 deinit_Block(&d->sourceContent);
254 deinit_String(&d->sourceMime); 258 deinit_String(&d->sourceMime);
255 iRelease(d->doc); 259 iRelease(d->doc);
260 deinit_PtrArray(&d->visiblePlayers);
256 deinit_PtrArray(&d->visibleLinks); 261 deinit_PtrArray(&d->visibleLinks);
257 delete_String(d->certSubject); 262 delete_String(d->certSubject);
258 delete_String(d->titleUser); 263 delete_String(d->titleUser);
@@ -333,7 +338,7 @@ static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) {
333 d->scrollY + height_Rect(bounds_Widget(constAs_Widget(d))) - margin }; 338 d->scrollY + height_Rect(bounds_Widget(constAs_Widget(d))) - margin };
334} 339}
335 340
336static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { 341static void addVisible_DocumentWidget_(void *context, const iGmRun *run) {
337 iDocumentWidget *d = context; 342 iDocumentWidget *d = context;
338 if (~run->flags & decoration_GmRunFlag && !run->imageId) { 343 if (~run->flags & decoration_GmRunFlag && !run->imageId) {
339 if (!d->firstVisibleRun) { 344 if (!d->firstVisibleRun) {
@@ -341,6 +346,9 @@ static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) {
341 } 346 }
342 d->lastVisibleRun = run; 347 d->lastVisibleRun = run;
343 } 348 }
349 if (run->audioId) {
350 pushBack_PtrArray(&d->visiblePlayers, run);
351 }
344 if (run->linkId && linkFlags_GmDocument(d->doc, run->linkId) & supportedProtocol_GmLinkFlag) { 352 if (run->linkId && linkFlags_GmDocument(d->doc, run->linkId) & supportedProtocol_GmLinkFlag) {
345 pushBack_PtrArray(&d->visibleLinks, run); 353 pushBack_PtrArray(&d->visibleLinks, run);
346 } 354 }
@@ -434,6 +442,18 @@ static void updateOutlineOpacity_DocumentWidget_(iDocumentWidget *d) {
434 animate_DocumentWidget_(d); 442 animate_DocumentWidget_(d);
435} 443}
436 444
445static void animatePlayingAudio_DocumentWidget_(void *widget) {
446 iDocumentWidget *d = widget;
447 iConstForEach(PtrArray, i, &d->visiblePlayers) {
448 const iGmRun *run = i.ptr;
449 iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->audioId);
450 if (isStarted_Player(plr) && !isPaused_Player(plr)) {
451 refresh_Widget(d);
452 addTicker_App(animatePlayingAudio_DocumentWidget_, d);
453 }
454 }
455}
456
437static void updateVisible_DocumentWidget_(iDocumentWidget *d) { 457static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
438 const iRangei visRange = visibleRange_DocumentWidget_(d); 458 const iRangei visRange = visibleRange_DocumentWidget_(d);
439 const iRect bounds = bounds_Widget(as_Widget(d)); 459 const iRect bounds = bounds_Widget(as_Widget(d));
@@ -443,10 +463,12 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
443 d->scrollY, 463 d->scrollY,
444 docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); 464 docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0);
445 clear_PtrArray(&d->visibleLinks); 465 clear_PtrArray(&d->visibleLinks);
466 clear_PtrArray(&d->visiblePlayers);
446 d->firstVisibleRun = NULL; 467 d->firstVisibleRun = NULL;
447 render_GmDocument(d->doc, visRange, addVisibleLink_DocumentWidget_, d); 468 render_GmDocument(d->doc, visRange, addVisible_DocumentWidget_, d);
448 updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); 469 updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window()));
449 updateSideOpacity_DocumentWidget_(d, iTrue); 470 updateSideOpacity_DocumentWidget_(d, iTrue);
471 animatePlayingAudio_DocumentWidget_(d);
450 /* Remember scroll positions of recently visited pages. */ { 472 /* Remember scroll positions of recently visited pages. */ {
451 iRecentUrl *recent = mostRecentUrl_History(d->mod.history); 473 iRecentUrl *recent = mostRecentUrl_History(d->mod.history);
452 if (recent && docSize && d->state == ready_RequestState) { 474 if (recent && docSize && d->state == ready_RequestState) {
@@ -1919,8 +1941,8 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
1919 return; 1941 return;
1920 } 1942 }
1921 else if (run->audioId) { 1943 else if (run->audioId) {
1922 /* Draw the audio player interface. */ 1944 /* Audio player UI is drawn afterwards as a dynamic overlay. */
1923 fillRect_Paint(&d->paint, moved_Rect(run->visBounds, origin), red_ColorId); 1945 //fillRect_Paint(&d->paint, moved_Rect(run->visBounds, origin), red_ColorId);
1924 return; 1946 return;
1925 } 1947 }
1926 enum iColorId fg = run->color; 1948 enum iColorId fg = run->color;
@@ -2239,6 +2261,119 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
2239 unsetClip_Paint(&p); 2261 unsetClip_Paint(&p);
2240} 2262}
2241 2263
2264iDeclareType(PlayerUI)
2265
2266struct Impl_PlayerUI {
2267 const iPlayer *player;
2268 iRect bounds;
2269 iRect playPauseRect;
2270 iRect rewindRect;
2271 iRect scrubberRect;
2272 iRect menuRect;
2273};
2274
2275static void init_PlayerUI(iPlayerUI *d, const iPlayer *player, iRect bounds) {
2276 d->player = player;
2277 d->bounds = bounds;
2278 const int height = height_Rect(bounds);
2279 d->playPauseRect = (iRect){ addX_I2(topLeft_Rect(bounds), gap_UI / 2), init1_I2(height) };
2280 d->rewindRect = (iRect){ topRight_Rect(d->playPauseRect), init1_I2(height) };
2281 d->menuRect = (iRect){ addX_I2(topRight_Rect(bounds), -height - gap_UI / 2), init1_I2(height) };
2282 d->scrubberRect = initCorners_Rect(topRight_Rect(d->rewindRect), bottomLeft_Rect(d->menuRect));
2283}
2284
2285static void drawPlayerButton_(iPaint *p, iRect rect, const char *label) {
2286 const iInt2 mouse = mouseCoord_Window(get_Window());
2287 const iBool isHover = contains_Rect(rect, mouse);
2288 const iBool isPressed = isHover && (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_LEFT) != 0;
2289 const int frame = (isPressed ? uiTextCaution_ColorId : isHover ? uiHeading_ColorId : uiAnnotation_ColorId);
2290 iRect frameRect = shrunk_Rect(rect, init_I2(gap_UI / 2, gap_UI));
2291 drawRect_Paint(p, frameRect, frame);
2292 if (isPressed) {
2293 fillRect_Paint(
2294 p,
2295 adjusted_Rect(shrunk_Rect(frameRect, divi_I2(gap2_UI, 2)), zero_I2(), one_I2()),
2296 frame);
2297 }
2298 const int fg = isPressed ? uiBackground_ColorId : frame;
2299 drawCentered_Text(uiContent_FontId, frameRect, iTrue, fg, "%s", label);
2300}
2301
2302static int drawSevenSegmentTime_(iInt2 pos, int color, int align, int seconds) { /* returns width */
2303 const uint32_t sevenSegmentDigit = 0x1fbf0;
2304 const int hours = seconds / 3600;
2305 const int mins = (seconds / 60) % 60;
2306 const int secs = seconds % 60;
2307 const int font = uiLabel_FontId;
2308 iString num;
2309 init_String(&num);
2310 if (hours) {
2311 appendChar_String(&num, sevenSegmentDigit + (hours % 10));
2312 appendChar_String(&num, ':');
2313 }
2314 appendChar_String(&num, sevenSegmentDigit + (mins / 10) % 10);
2315 appendChar_String(&num, sevenSegmentDigit + (mins % 10));
2316 appendChar_String(&num, ':');
2317 appendChar_String(&num, sevenSegmentDigit + (secs / 10) % 10);
2318 appendChar_String(&num, sevenSegmentDigit + (secs % 10));
2319 iInt2 size = advanceRange_Text(font, range_String(&num));
2320 if (align == right_Alignment) {
2321 pos.x -= size.x;
2322 }
2323 drawRange_Text(font, pos, color, range_String(&num));
2324 deinit_String(&num);
2325 return size.x;
2326}
2327
2328static void draw_PlayerUI(iPlayerUI *d, iPaint *p) {
2329 const int playerBackground_ColorId = uiBackground_ColorId;
2330 const int playerFrame_ColorId = uiSeparator_ColorId;
2331 fillRect_Paint(p, d->bounds, playerBackground_ColorId);
2332 drawRect_Paint(p, d->bounds, playerFrame_ColorId);
2333 drawPlayerButton_(p, d->playPauseRect, isPaused_Player(d->player) ? "\U0001f782" : "\u23f8");
2334 drawPlayerButton_(p, d->rewindRect, "\u23ee");
2335 drawPlayerButton_(p, d->menuRect, "\U0001d362");
2336 const int hgt = lineHeight_Text(uiLabel_FontId);
2337 const int yMid = mid_Rect(d->scrubberRect).y;
2338 const float playTime = time_Player(d->player);
2339 const float totalTime = duration_Player(d->player);
2340 const int bright = uiHeading_ColorId;
2341 const int dim = uiAnnotation_ColorId;
2342 int leftWidth = drawSevenSegmentTime_(
2343 init_I2(left_Rect(d->scrubberRect) + 2 * gap_UI, yMid - hgt / 2),
2344 isPaused_Player(d->player) ? dim : bright,
2345 left_Alignment,
2346 iRound(playTime));
2347 int rightWidth = drawSevenSegmentTime_(
2348 init_I2(right_Rect(d->scrubberRect) - 2 * gap_UI, yMid - hgt / 2),
2349 dim,
2350 right_Alignment,
2351 iRound(totalTime));
2352 /* Scrubber. */
2353 const int s1 = left_Rect(d->scrubberRect) + leftWidth + 6 * gap_UI;
2354 const int s2 = right_Rect(d->scrubberRect) - rightWidth - 6 * gap_UI;
2355 const float normPos = totalTime > 0 ? playTime / totalTime : 0.0f;
2356 const int part = (s2 - s1) * normPos;
2357 drawHLine_Paint(p, init_I2(s1, yMid), part, bright);
2358 drawHLine_Paint(p, init_I2(s1 + part, yMid), (s2 - s1) - part, dim);
2359 draw_Text(uiLabel_FontId,
2360 init_I2(s1 * (1.0f - normPos) + s2 * normPos - hgt / 3, yMid - hgt / 2),
2361 bright,
2362 "\u23fa");
2363}
2364
2365static void drawAudioPlayers_DocumentWidget_(const iDocumentWidget *d, iPaint *p) {
2366 const iRect docBounds = documentBounds_DocumentWidget_(d);
2367 iConstForEach(PtrArray, i, &d->visiblePlayers) {
2368 const iGmRun * run = i.ptr;
2369 const iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->audioId);
2370 const iRect rect = moved_Rect(run->bounds, addY_I2(topLeft_Rect(docBounds), -d->scrollY));
2371 iPlayerUI ui;
2372 init_PlayerUI(&ui, plr, rect);
2373 draw_PlayerUI(&ui, p);
2374 }
2375}
2376
2242static void draw_DocumentWidget_(const iDocumentWidget *d) { 2377static void draw_DocumentWidget_(const iDocumentWidget *d) {
2243 const iWidget *w = constAs_Widget(d); 2378 const iWidget *w = constAs_Widget(d);
2244 const iRect bounds = bounds_Widget(w); 2379 const iRect bounds = bounds_Widget(w);
@@ -2297,7 +2432,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
2297 } 2432 }
2298 } 2433 }
2299 endTarget_Paint(&ctx.paint); 2434 endTarget_Paint(&ctx.paint);
2300 fflush(stdout); 2435// fflush(stdout);
2301 } 2436 }
2302 validate_VisBuf(visBuf); 2437 validate_VisBuf(visBuf);
2303 clear_PtrSet(d->invalidRuns); 2438 clear_PtrSet(d->invalidRuns);
@@ -2314,6 +2449,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
2314 render_GmDocument(d->doc, vis, drawMark_DrawContext_, &ctx); 2449 render_GmDocument(d->doc, vis, drawMark_DrawContext_, &ctx);
2315 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); 2450 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
2316 } 2451 }
2452 drawAudioPlayers_DocumentWidget_(d, &ctx.paint);
2317 unsetClip_Paint(&ctx.paint); 2453 unsetClip_Paint(&ctx.paint);
2318 /* Fill the top and bottom, in case the document is short. */ 2454 /* Fill the top and bottom, in case the document is short. */
2319 if (yTop > top_Rect(bounds)) { 2455 if (yTop > top_Rect(bounds)) {