diff --git a/Android.mk b/Android.mk index ced4092156..f5266360a0 100644 --- a/Android.mk +++ b/Android.mk @@ -16,6 +16,7 @@ LOCAL_SRC_FILES :=\ base/colorutil.cpp \ base/error_context.cpp \ base/stringutil.cpp \ + data/compression.cpp \ ext/rg_etc1/rg_etc1.cpp \ ext/cityhash/city.cpp \ ext/sha1/sha1.cpp \ @@ -32,6 +33,7 @@ LOCAL_SRC_FILES :=\ file/zip_read.cpp \ json/json_writer.cpp \ i18n/i18n.cpp \ + input/gesture_detector.cpp \ math/math_util.cpp \ math/curves.cpp \ math/lin/aabb.cpp.arm \ diff --git a/android/app-android.cpp b/android/app-android.cpp index 2d9a7dc322..9fb4009f0d 100644 --- a/android/app-android.cpp +++ b/android/app-android.cpp @@ -263,8 +263,8 @@ extern "C" void Java_com_henrikrydgard_libnative_NativeRenderer_displayRender(JN lock_guard guard(input_state.lock); input_state.pad_lstick_x = left_joystick_x_async; input_state.pad_lstick_y = left_joystick_y_async; - NativeUpdate(input_state); } + NativeUpdate(input_state); { lock_guard guard(input_state.lock); @@ -319,17 +319,14 @@ extern "C" jint Java_com_henrikrydgard_libnative_NativeApp_audioRender(JNIEnv* e extern "C" void JNICALL Java_com_henrikrydgard_libnative_NativeApp_touch (JNIEnv *, jclass, float x, float y, int code, int pointerId) { + ELOG("Touch Enter %i", pointerId); lock_guard guard(input_state.lock); - if (pointerId >= MAX_POINTERS) { - ELOG("Too many pointers: %i", pointerId); - return; // We ignore 8+ pointers entirely. - } float scaledX = (int)(x * dp_xscale); // why the (int) cast? float scaledY = (int)(y * dp_yscale); - input_state.pointer_x[pointerId] = scaledX; - input_state.pointer_y[pointerId] = scaledY; + TouchInput touch; + touch.id = pointerId; touch.x = scaledX; touch.y = scaledY; if (code == 1) { @@ -341,8 +338,16 @@ extern "C" void JNICALL Java_com_henrikrydgard_libnative_NativeApp_touch } else { touch.flags = TOUCH_MOVE; } - NativeTouch(touch); + + if (pointerId >= MAX_POINTERS) { + ELOG("Too many pointers: %i", pointerId); + return; // We ignore 8+ pointers entirely. + } + input_state.pointer_x[pointerId] = scaledX; + input_state.pointer_y[pointerId] = scaledY; input_state.mouse_valid = true; + NativeTouch(touch); + ELOG("Touch Exit %i", pointerId); } static void AsyncDown(int padbutton) { diff --git a/base/buffer.cpp b/base/buffer.cpp index 9d0c160fcf..f618727c02 100644 --- a/base/buffer.cpp +++ b/base/buffer.cpp @@ -46,8 +46,10 @@ void Buffer::AppendValue(int value) { void Buffer::Take(size_t length, std::string *dest) { CHECK_LE(length, data_.size()); dest->resize(length); - memcpy(&(*dest)[0], &data_[0], length); - data_.erase(data_.begin(), data_.begin() + length); + if (length > 0) { + memcpy(&(*dest)[0], &data_[0], length); + data_.erase(data_.begin(), data_.begin() + length); + } } int Buffer::TakeLineCRLF(std::string *dest) { diff --git a/base/stringutil.h b/base/stringutil.h index fe607fa9aa..d56880189a 100644 --- a/base/stringutil.h +++ b/base/stringutil.h @@ -63,10 +63,14 @@ public: // Other simple string utilities. inline bool startsWith(const std::string &str, const std::string &what) { + if (str.size() < what.size()) + return false; return str.substr(0, what.size()) == what; } inline bool endsWith(const std::string &str, const std::string &what) { + if (str.size() < what.size()) + return false; return str.substr(str.size() - what.size()) == what; } diff --git a/data/compression.cpp b/data/compression.cpp new file mode 100644 index 0000000000..1446b4993c --- /dev/null +++ b/data/compression.cpp @@ -0,0 +1,103 @@ +// Little utility functions for data compression. +// Taken from http://panthema.net/2007/0328-ZLibString.html + + +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" + +/** Compress a STL string using zlib with given compression level and return +* the binary data. */ +bool compress_string(const std::string& str, std::string *dest, int compressionlevel) { + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + if (deflateInit(&zs, compressionlevel) != Z_OK) { + ELOG("deflateInit failed while compressing."); + return false; + } + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); // set the z_stream's input + + int ret; + char outbuffer[32768]; + std::string outstring; + + // retrieve the compressed bytes blockwise + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = deflate(&zs, Z_FINISH); + + if (outstring.size() < zs.total_out) { + // append the block to the output string + outstring.append(outbuffer, + zs.total_out - outstring.size()); + } + } while (ret == Z_OK); + + deflateEnd(&zs); + + if (ret != Z_STREAM_END) { // an error occurred that was not EOF + std::ostringstream oss; + oss << "Exception during zlib compression: (" << ret << ") " << zs.msg; + return false; + } + + *dest = outstring; + return true; +} + +/** Decompress an STL string using zlib and return the original data. */ +bool decompress_string(const std::string& str, std::string *dest) { + if (!str.size()) + return false; + + z_stream zs; // z_stream is zlib's control structure + memset(&zs, 0, sizeof(zs)); + + // modification by hrydgard: inflateInit2, 16+MAXWBITS makes it read gzip data too + if (inflateInit2(&zs, 32+MAX_WBITS) != Z_OK) { + ELOG("inflateInit failed while decompressing."); + return false; + } + + zs.next_in = (Bytef*)str.data(); + zs.avail_in = str.size(); + + int ret; + char outbuffer[32768]; + std::string outstring; + + // get the decompressed bytes blockwise using repeated calls to inflate + do { + zs.next_out = reinterpret_cast(outbuffer); + zs.avail_out = sizeof(outbuffer); + + ret = inflate(&zs, 0); + + if (outstring.size() < zs.total_out) { + outstring.append(outbuffer, + zs.total_out - outstring.size()); + } + + } while (ret == Z_OK); + + inflateEnd(&zs); + + if (ret != Z_STREAM_END) { // an error occurred that was not EOF + std::ostringstream oss; + ELOG("Exception during zlib decompression: (%i) %s", ret, zs.msg); + return false; + } + + *dest = outstring; +} \ No newline at end of file diff --git a/math/compression.h b/data/compression.h similarity index 71% rename from math/compression.h rename to data/compression.h index f9e457c8d0..2199c5591e 100644 --- a/math/compression.h +++ b/data/compression.h @@ -1,5 +1,11 @@ #pragma once +#include + +bool compress_string(const std::string& str, std::string *dest, int compressionlevel = Z_BEST_COMPRESSION); +bool decompress_string(const std::string& str, std::string *dest); + + // Delta encoding/decoding - many formats benefit from a pass of this before zlibbing. // WARNING : Do not use these with floating point data, especially not float16... diff --git a/native.vcxproj b/native.vcxproj index bf22e8df3d..b06a52b24a 100644 --- a/native.vcxproj +++ b/native.vcxproj @@ -189,6 +189,7 @@ + @@ -223,7 +224,6 @@ - @@ -293,6 +293,7 @@ + AnySuitable true diff --git a/native.vcxproj.filters b/native.vcxproj.filters index c6f8ae6f15..cae63ba799 100644 --- a/native.vcxproj.filters +++ b/native.vcxproj.filters @@ -131,9 +131,6 @@ file - - math - gfx @@ -272,6 +269,9 @@ ui + + data + @@ -480,6 +480,9 @@ ui + + data + diff --git a/net/http_client.cpp b/net/http_client.cpp index 4963fc01d2..17e7ac8a35 100644 --- a/net/http_client.cpp +++ b/net/http_client.cpp @@ -17,6 +17,7 @@ #include "base/logging.h" #include "base/buffer.h" #include "base/stringutil.h" +#include "data/compression.h" #include "net/resolve.h" #include "net/url.h" @@ -84,8 +85,6 @@ void Connection::Disconnect() { if ((intptr_t)sock_ != -1) { closesocket(sock_); sock_ = -1; - } else { - WLOG("Socket was already disconnected."); } } @@ -99,38 +98,103 @@ void Connection::Reconnect() { namespace http { Client::Client() { + } + Client::~Client() { Disconnect(); } -#define USERAGENT "METAGET 1.0" +// TODO: do something sane here +#define USERAGENT "NATIVEAPP 1.0" + + +void DeChunk(Buffer *inbuffer, Buffer *outbuffer) { + while (true) { + std::string line; + inbuffer->TakeLineCRLF(&line); + if (!line.size()) + return; + int chunkSize; + sscanf(line.c_str(), "%x", &chunkSize); + if (chunkSize) { + std::string data; + inbuffer->Take(chunkSize, &data); + outbuffer->Append(data); + } else { + // a zero size chunk should mean the end. + inbuffer->clear(); + return; + } + inbuffer->Skip(2); + } +} int Client::GET(const char *resource, Buffer *output) { Buffer buffer; - const char *tpl = "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: " USERAGENT "\r\n\r\n"; + const char *tpl = + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: " USERAGENT "\r\n" + "Accept: */*\r\n" + "Accept-Encoding: gzip\r\n" + "Connection: close\r\n" + "\r\n"; + buffer.Printf(tpl, resource, host_.c_str()); bool flushed = buffer.FlushSocket(sock()); if (!flushed) { return -1; // TODO error code. } - // Snarf all the data we can. - if (!output->ReadAll(sock())) + Buffer readbuf; + + // Snarf all the data we can into RAM. A little unsafe but hey. + if (!readbuf.ReadAll(sock())) return -1; // Grab the first header line that contains the http code. // Skip the header. TODO: read HTTP code and file size so we can make progress bars. - std::string firstline; - CHECK_GT(output->TakeLineCRLF(&firstline), 0); - int code = atoi(&firstline[9]); + std::string line; + CHECK_GT(readbuf.TakeLineCRLF(&line), 0); + int code = atoi(&line[line.find(" ") + 1]); - while (output->SkipLineCRLF() > 0) - ; + bool gzip = false; + int contentLength = 0; + while (true) { + int sz = readbuf.TakeLineCRLF(&line); + if (!sz) + break; + if (startsWith(line, "Content-Length:")) { + contentLength = atoi(&line[16]); + } else if (startsWith(line, "Content-Encoding:")) { + if (line.find("gzip") != std::string::npos) { + gzip = true; + } + } + } + + // output now contains the rest of the reply. Dechunk it. + DeChunk(&readbuf, output); + + // If it's gzipped, we decompress it and put it back in the buffer. + if (gzip) { + std::string compressed; + output->TakeAll(&compressed); + // What is this garbage? + //if (compressed[0] == 0x8e) +// compressed = compressed.substr(4); + std::string decompressed; + bool result = decompress_string(compressed, &decompressed); + if (!result) { + ELOG("Error decompressing using zlib"); + return -1; + } + output->Append(decompressed); + } - // output now contains the rest of the reply. return code; } diff --git a/ui/ui_context.cpp b/ui/ui_context.cpp index 00fd157cf8..9a92458971 100644 --- a/ui/ui_context.cpp +++ b/ui/ui_context.cpp @@ -86,10 +86,11 @@ void UIContext::PopScissor() { void UIContext::ActivateTopScissor() { if (scissorStack_.size()) { const Bounds &bounds = scissorStack_.back(); - int x = g_dpi_scale * bounds.x; - int y = g_dpi_scale * (dp_yres - bounds.y2()); - int w = g_dpi_scale * bounds.w; - int h = g_dpi_scale * bounds.h; + float scale = 1.0f / g_dpi_scale; + int x = scale * bounds.x; + int y = scale * (dp_yres - bounds.y2()); + int w = scale * bounds.w; + int h = scale * bounds.h; glstate.scissorRect.set(x,y,w,h); glstate.scissorTest.enable(); diff --git a/ui/viewgroup.h b/ui/viewgroup.h index b9bb0038bd..1ad1a4a067 100644 --- a/ui/viewgroup.h +++ b/ui/viewgroup.h @@ -109,7 +109,7 @@ private: class LinearLayout : public ViewGroup { public: LinearLayout(Orientation orientation, LayoutParams *layoutParams = 0) - : ViewGroup(layoutParams), spacing_(5), orientation_(orientation), defaultMargins_(0) {} + : ViewGroup(layoutParams), orientation_(orientation), defaultMargins_(0), spacing_(10) {} void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert); void Layout(); @@ -153,7 +153,13 @@ private: class ScrollView : public ViewGroup { public: ScrollView(Orientation orientation, LayoutParams *layoutParams = 0) : - ViewGroup(layoutParams), orientation_(orientation), scrollPos_(0), scrollTarget_(0), scrollToTarget_(false) {} + ViewGroup(layoutParams), + orientation_(orientation), + scrollPos_(0), + scrollStart_(0), + scrollMax_(0), + scrollTarget_(0), + scrollToTarget_(false) {} void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert); void Layout();