UI, http with gzip, etc.

This commit is contained in:
Henrik Rydgard 2013-06-04 22:05:17 +02:00
parent 3b9750ee49
commit a509d38357
11 changed files with 229 additions and 32 deletions

View File

@ -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 \

View File

@ -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) {

View File

@ -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) {

View File

@ -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;
}

103
data/compression.cpp Normal file
View File

@ -0,0 +1,103 @@
// Little utility functions for data compression.
// Taken from http://panthema.net/2007/0328-ZLibString.html
#include <string>
#include <stdexcept>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <zlib.h>
#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<Bytef*>(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<Bytef*>(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;
}

View File

@ -1,5 +1,11 @@
#pragma once
#include <zlib.h>
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...

View File

@ -189,6 +189,7 @@
<ClInclude Include="base\stats.h" />
<ClInclude Include="base\stringutil.h" />
<ClInclude Include="base\timeutil.h" />
<ClInclude Include="data\compression.h" />
<ClInclude Include="data\listable.h" />
<ClInclude Include="ext\cityhash\city.h" />
<ClInclude Include="ext\cityhash\citycrc.h" />
@ -223,7 +224,6 @@
<ClInclude Include="input\gesture_detector.h" />
<ClInclude Include="input\input_state.h" />
<ClInclude Include="json\json_writer.h" />
<ClInclude Include="math\compression.h" />
<ClInclude Include="math\curves.h" />
<ClInclude Include="math\geom2d.h" />
<ClInclude Include="math\lin\aabb.h" />
@ -293,6 +293,7 @@
</ClCompile>
<ClCompile Include="base\stringutil.cpp" />
<ClCompile Include="base\timeutil.cpp" />
<ClCompile Include="data\compression.cpp" />
<ClCompile Include="ext\cityhash\city.cpp">
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AnySuitable</InlineFunctionExpansion>
<IntrinsicFunctions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</IntrinsicFunctions>

View File

@ -131,9 +131,6 @@
<ClInclude Include="file\file_util.h">
<Filter>file</Filter>
</ClInclude>
<ClInclude Include="math\compression.h">
<Filter>math</Filter>
</ClInclude>
<ClInclude Include="gfx_es2\vertex_format.h">
<Filter>gfx</Filter>
</ClInclude>
@ -272,6 +269,9 @@
<ClInclude Include="ui\ui_screen.h">
<Filter>ui</Filter>
</ClInclude>
<ClInclude Include="data\compression.h">
<Filter>data</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="gfx\gl_debug_log.cpp">
@ -480,6 +480,9 @@
<ClCompile Include="ui\ui_screen.cpp">
<Filter>ui</Filter>
</ClCompile>
<ClCompile Include="data\compression.cpp">
<Filter>data</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="gfx">

View File

@ -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;
}

View File

@ -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();

View File

@ -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();