If a timer expires while stopped, it should trigger when TIMER ON is
run. Instead, on QB64 it triggers randomly after the TIMER ON happens.
The basic issue is that `qbevent` needs to be set to trigger the timer,
but TIMER ON doesn't do that. The regular timer logic that does that
already set it when the timer expired while sleeping, so it won't set it
again. The simplest solution is to just alway set qbevent = 1 when TIMER
ON is done. It's slightly less efficent but doesn't hurt to set it even
when there are no timers that expired.
Fixes: #293
The command Sleep is supposed to allow timers to trigger while the
program is sleeping on the delay. This is achieved in QB64 by having
commands that do delays manually call evnt() to trigger events if they
come up (of which timers are one).
Sleep has a custom implementation for console programs on Windows which
doesn't do this, so I redid the logic so that it calls evnt() at regular
intervals while waiting for input. Additionally, due to now calling
evnt() we also need to check if we should exit sleep early due to an
evnt() firing.
Fixes: #294
Timer's were not firing at the right time if they were started shortly
after the program started, instead they would fire at twice the interval
time (and then work correctly after that).
The issue was a mistaken assumption about `time_now`, with the idea that
if `last_time == 0` then `time_now` will be large enough such that the
interval check will pass. This is wrong because in most cases `time_now`
starts at zero at program start, so when `last_time == 0` it will take
one full interval of the timer before `time_now` is large enough for the
interval check to pass (at which point the timer is initialized and runs
normally).
This simply refactors the timer logic so that `last_time == 0` is
checked first, rather than if the interval has expired. This doesn't
change how the normal logic works, but ensures that the value of
`time_now` does not matter for initializing a timer.
Fixes: #273
Fairly simple, MacOS High Sierra's libcurl version is too old and not
have `CURLINFO_CONTENT_LENGTH_DOWNLOAD_T`. This adds a version check to
use the older version of that command which gives back a double instead.
Fixes: #287
Currently main() includes logic that is intended to sync time() with
GetTicks() for the purpose of using GetTicks() to get millisecond
accuracy with time(), which only has second accuracy. Unfortunately, the
'syncing' up of these time sources results in an average of a half
second delay in starting a QB64-PE program.
This logic is easly replaced with std::chrono, which provides a real
time clock which is also millisecond accurate. That removes the need to
use time() and GetTicks() together to get millisecond accuracy, and
means the delay syncing them is no longer necessary.
I also separated most of the "delay" and "time" related functions into
datetime.cpp, and included the new std::chrono code into that file.
Since I needed to call some of the rounding functions in datetime.cpp I
also moved that stuff out into its own .cpp and header files to clean
things up a bit.
Fixes: #282
The icon image creation is actually fairly expensive because the first
time you create a 32-bit image init_blend() is called, which is fairly
slow. Since only sub__icon makes use of these images (non-Windows
platforms and $Console:Only programs can't even use them) it's easy
enough to move the creation into sub__icon so the creation cost is
avoided on startup.
Fairly simple, the finished entry is free'd and then removed from the
list, but that order results in us accessing the entry's next member
after it has been free'd. Swapping the order of the operations fixes the
issue.
Fixes: #281
The commands _ScreenX and _ScreenY got significantly slower due to the
need to wait for the GLUT thread to wake up and execute the glutGet()
command for them. We've already seen a few programs (including the IDE)
where this behavior completely grinds the program to a halt, so we
definitely can't keep it.
The simple solution here is to not call glutGet() on every _ScreenX/Y
command. Instead every time the idle/timer function runs we get the
current values for the relevant glutGet() variables and store them.
libqb_glut_get() then checks if the value being read is one of the ones
we read in the idle/timer functionand if so just returns the last read
value. By doing it this way the commands no longer has to wait on the
GLUT thread for the result.
Fairly straightfowrad, programs were randomly seg faulting on exit. This
was happening due to GLUT registering a cleanup function via atexit(),
which then gets called when exit() is called.
The issue happens when exit() is called on a thread other than the GLUT
thread, which leads to the exit() call then attempting to cleanup GLUT
while the other thread is still using it, which randomly leads to seg
faults.
Fixing this is slightly annoying. We cannot stop the GLUT thread, as
the basic GLUT API (which is used on Mac OS) simply does not offer a way
to exit the glutMainLoop() call. Thus the simplest solution is to simply
make sure we call exit() on the GLUT thread, which we can fairly easily
due via the message queue.
That being the case, a new libqb_exit() API was added, which simply
regsiters the GLUT exit message and then waits for the program to end.
The atexit() handler then runs on the GLUT thread and everything works
out fine.
In the future we probably should redo the exit logic a bit so that all
the threads are actually stopped/joined to ensure the exit process is
consistent, however this is good enough for now. Also, there's plenty of
error states which call exit() which I did not address.
With the recent changes to libqb.cpp to pull out some of the GLUT logic,
the only actual Objective-C in libqb.cpp was pulled out. That being the
case, it's no longer necessary to have libqb.mm for compiling libqb.cpp,
so we're removing it to simplify the compliation logic a bit.