Merge branch 'feature/mstsc' of github.com:tp4444eleport into feature/mstsc
|
@ -28,8 +28,10 @@ __pycache__
|
|||
**/.idea/misc.xml
|
||||
**/.idea/dictionaries
|
||||
**/.idea/watcherTasks.xml
|
||||
**/.idea/dataSources.*
|
||||
**/.idea/inspectionProfiles
|
||||
**/.idea/codeStyles
|
||||
**/.idea/dataSources
|
||||
**/.idea/inspectionProfiles
|
||||
**/.idea/vcs.xml
|
||||
**/.idea/modules.xml
|
||||
|
@ -100,3 +102,7 @@ profile
|
|||
*.moved-aside
|
||||
/server/share/tmp
|
||||
|
||||
/server/tp_core/testssh/Debug
|
||||
/server/tp_core/testssh/Release
|
||||
/external/zlib
|
||||
/client/tools/qt-redist
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
################################################################
|
||||
# Basic settings.
|
||||
################################################################
|
||||
VER_PYTHON="3.7.0"
|
||||
VER_PYTHON="3.7.4"
|
||||
VER_PYTHON_SHORT="3.7"
|
||||
VER_OPENSSL="1.0.2p"
|
||||
VER_SQLITE="3250000"
|
||||
VER_OPENSSL="1.0.2s"
|
||||
VER_SQLITE="3290000"
|
||||
VER_ZLIB="1.2.11"
|
||||
VER_PYTHON_LIB="${VER_PYTHON_SHORT}m"
|
||||
|
||||
|
@ -77,7 +77,7 @@ function step_download_files()
|
|||
|
||||
dlfile "python source tarball" "https://www.python.org/ftp/python/${VER_PYTHON}/" "Python-${VER_PYTHON}.tgz" ${PATH_DOWNLOAD}
|
||||
dlfile "openssl source tarball" "https://www.openssl.org/source/" "openssl-${VER_OPENSSL}.tar.gz" ${PATH_DOWNLOAD}
|
||||
dlfile "sqlite source tarball" "http://sqlite.org/2018/" "sqlite-autoconf-${VER_SQLITE}.tar.gz" ${PATH_DOWNLOAD}
|
||||
dlfile "sqlite source tarball" "http://sqlite.org/2019/" "sqlite-autoconf-${VER_SQLITE}.tar.gz" ${PATH_DOWNLOAD}
|
||||
dlfile "zlib source tarball" "https://www.zlib.net/" "zlib-${VER_ZLIB}.tar.gz" ${PATH_DOWNLOAD}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,21 +13,24 @@ class BuilderBase:
|
|||
def __init__(self):
|
||||
self.out_dir = ''
|
||||
|
||||
def build_exe(self):
|
||||
pass
|
||||
def build_assist(self):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_player(self):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_rdp(self):
|
||||
pass
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_installer(self):
|
||||
pass
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
|
||||
class BuilderWin(BuilderBase):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def build_exe(self):
|
||||
def build_assist(self):
|
||||
cc.i('build tp_assist...')
|
||||
sln_file = os.path.join(env.root_path, 'client', 'tp_assist_win', 'tp_assist.vs2017.sln')
|
||||
out_file = os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path, 'tp_assist.exe')
|
||||
|
@ -36,6 +39,15 @@ class BuilderWin(BuilderBase):
|
|||
utils.msvc_build(sln_file, 'tp_assist', ctx.target_path, ctx.bits_path, False)
|
||||
utils.ensure_file_exists(out_file)
|
||||
|
||||
def build_player(self):
|
||||
cc.i('build tp-player...')
|
||||
prj_path = os.path.join(env.root_path, 'client', 'tp-player')
|
||||
out_file = os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path, 'tp-player.exe')
|
||||
if os.path.exists(out_file):
|
||||
utils.remove(out_file)
|
||||
utils.qt_build_win(prj_path, 'tp-player', ctx.bits_path, ctx.target_path)
|
||||
utils.ensure_file_exists(out_file)
|
||||
|
||||
# def build_rdp(self):
|
||||
# cc.n('build tp_rdp...')
|
||||
# sln_file = os.path.join(ROOT_PATH, 'client', 'tp_rdp', 'tp_rdp.2015.sln')
|
||||
|
@ -74,12 +86,13 @@ class BuilderWin(BuilderBase):
|
|||
utils.makedirs(tmp_cfg_path)
|
||||
|
||||
utils.copy_file(os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path), tmp_app_path, 'tp_assist.exe')
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, ('tp-assist.windows.json', 'tp-assist.json'))
|
||||
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, ('tp-assist.windows.json', 'tp-assist.json'))
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'cacert.cer')
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'localhost.key')
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'localhost.pem')
|
||||
|
||||
# assist configuration web page
|
||||
utils.copy_ex(os.path.join(env.root_path, 'client', 'tp_assist_win'), tmp_app_path, 'site')
|
||||
|
||||
utils.makedirs(os.path.join(tmp_app_path, 'tools', 'putty'))
|
||||
|
@ -91,13 +104,35 @@ class BuilderWin(BuilderBase):
|
|||
|
||||
utils.makedirs(os.path.join(tmp_app_path, 'tools', 'tprdp'))
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-client.exe')
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-replay.exe')
|
||||
# utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-replay.exe')
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'libeay32.dll')
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'ssleay32.dll')
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'msvcr120.dll')
|
||||
|
||||
utils.copy_file(os.path.join(env.root_path, 'client', 'tools'), os.path.join(tmp_app_path, 'tools'), 'securecrt-telnet.vbs')
|
||||
|
||||
# tp-player
|
||||
utils.copy_file(os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path), tmp_app_path, 'tp-player.exe')
|
||||
|
||||
# qt-redist
|
||||
qt_redist_path = os.path.join(env.root_path, 'client', 'tools', 'qt-redist')
|
||||
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Core.dll')
|
||||
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Gui.dll')
|
||||
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Network.dll')
|
||||
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Widgets.dll')
|
||||
utils.copy_ex(os.path.join(qt_redist_path, 'platforms'), os.path.join(tmp_app_path, 'platforms'))
|
||||
utils.copy_ex(os.path.join(qt_redist_path, 'styles'), os.path.join(tmp_app_path, 'styles'))
|
||||
utils.copy_ex(os.path.join(qt_redist_path, 'translations'), os.path.join(tmp_app_path, 'translations'))
|
||||
|
||||
# zlib
|
||||
suffix = 'd' if ctx.target_path == 'debug' else ''
|
||||
utils.copy_file(os.path.join(env.root_path, 'external', 'zlib', 'build', ctx.target_path), tmp_app_path, 'zlib{}.dll'.format(suffix))
|
||||
|
||||
# openssl
|
||||
utils.copy_file(os.path.join(env.root_path, 'external', 'openssl', 'bin'), tmp_app_path, 'libcrypto-1_1.dll')
|
||||
utils.copy_file(os.path.join(env.root_path, 'external', 'openssl', 'bin'), tmp_app_path, 'libssl-1_1.dll')
|
||||
|
||||
# final build
|
||||
utils.nsis_build(os.path.join(env.root_path, 'dist', 'client', 'windows', 'assist', 'installer.nsi'))
|
||||
|
||||
|
||||
|
@ -105,7 +140,7 @@ class BuilderMacOS(BuilderBase):
|
|||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def build_exe(self):
|
||||
def build_assist(self):
|
||||
cc.i('build tp_assist...')
|
||||
|
||||
configuration = ctx.target_path.capitalize()
|
||||
|
@ -169,7 +204,7 @@ class BuilderLinux(BuilderBase):
|
|||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def build_exe(self):
|
||||
def build_assist(self):
|
||||
cc.e('not support linux.')
|
||||
|
||||
# def build_rdp(self):
|
||||
|
@ -215,7 +250,8 @@ def main():
|
|||
builder = gen_builder(ctx.host_os)
|
||||
|
||||
if 'exe' in argv:
|
||||
builder.build_exe()
|
||||
builder.build_assist()
|
||||
builder.build_player()
|
||||
# elif 'rdp' in argv:
|
||||
# builder.build_rdp()
|
||||
elif 'installer' in argv:
|
||||
|
|
|
@ -27,19 +27,25 @@ class BuilderBase:
|
|||
|
||||
def build_jsoncpp(self):
|
||||
file_name = 'jsoncpp-{}.zip'.format(env.ver_jsoncpp)
|
||||
if not utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name):
|
||||
return
|
||||
# if not utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name):
|
||||
# return
|
||||
self._build_jsoncpp(file_name)
|
||||
|
||||
def _download_jsoncpp(self, file_name):
|
||||
return utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_jsoncpp(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_mongoose(self):
|
||||
file_name = 'mongoose-{}.zip'.format(env.ver_mongoose)
|
||||
if not utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name):
|
||||
return
|
||||
# if not utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name):
|
||||
# return
|
||||
self._build_mongoose(file_name)
|
||||
|
||||
def _download_mongoose(self, file_name):
|
||||
return utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_mongoose(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
|
@ -47,38 +53,64 @@ class BuilderBase:
|
|||
file_name = 'openssl-{}.zip'.format(env.ver_ossl)
|
||||
self._build_openssl(file_name)
|
||||
|
||||
def _build_openssl(self, file_name):
|
||||
def _download_openssl(self, file_name):
|
||||
_alt_ver = '_'.join(env.ver_ossl.split('.'))
|
||||
if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
|
||||
cc.e("can not download openssl source tarball.")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_openssl(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
# _alt_ver = '_'.join(env.ver_ossl.split('.'))
|
||||
# if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
|
||||
# cc.e("can not download openssl source tarball.")
|
||||
# return False
|
||||
# else:
|
||||
# return True
|
||||
|
||||
def build_libuv(self):
|
||||
file_name = 'libuv-{}.zip'.format(env.ver_libuv)
|
||||
if not utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name):
|
||||
return
|
||||
# if not utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name):
|
||||
# return
|
||||
self._build_libuv(file_name)
|
||||
|
||||
def _download_libuv(self, file_name):
|
||||
return utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_libuv(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_mbedtls(self):
|
||||
file_name = 'mbedtls-mbedtls-{}.zip'.format(env.ver_mbedtls)
|
||||
if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name):
|
||||
return
|
||||
# if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name):
|
||||
# return
|
||||
self._build_mbedtls(file_name)
|
||||
|
||||
def _download_mbedtls(self, file_name):
|
||||
return utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_mbedtls(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_zlib(self):
|
||||
file_name = 'zlilb{}.zip'.format(env.ver_zlib_number)
|
||||
# if not utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name):
|
||||
# return
|
||||
self._build_zlib(file_name)
|
||||
|
||||
def _download_zlib(self, file_name):
|
||||
return utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_zlib(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
def build_libssh(self):
|
||||
file_name = 'libssh-{}.zip'.format(env.ver_libssh)
|
||||
if not utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name):
|
||||
return
|
||||
# if not utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name):
|
||||
# return
|
||||
self._build_libssh(file_name)
|
||||
|
||||
def _download_libssh(self, file_name):
|
||||
return utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name)
|
||||
|
||||
def _build_libssh(self, file_name):
|
||||
cc.e("this is a pure-virtual function.")
|
||||
|
||||
|
@ -103,9 +135,10 @@ class BuilderWin(BuilderBase):
|
|||
self.MBEDTLS_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mbedtls')
|
||||
self.LIBUV_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libuv')
|
||||
self.LIBSSH_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libssh')
|
||||
self.ZLIB_PATH_SRC = os.path.join(PATH_EXTERNAL, 'zlib')
|
||||
|
||||
def _prepare_python(self):
|
||||
cc.n('prepare python header files ...', end='')
|
||||
cc.n('prepare python header files ... ', end='')
|
||||
|
||||
if os.path.exists(os.path.join(PATH_EXTERNAL, 'python', 'include', 'Python.h')):
|
||||
cc.w('already exists, skip.')
|
||||
|
@ -125,53 +158,85 @@ class BuilderWin(BuilderBase):
|
|||
utils.copy_ex(_header_path, os.path.join(PATH_EXTERNAL, 'python', 'include'))
|
||||
|
||||
def _build_openssl(self, file_name):
|
||||
cc.n('build openssl static library from source code... ')
|
||||
|
||||
if not super()._build_openssl(file_name):
|
||||
return
|
||||
|
||||
_chk_output = [
|
||||
os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
|
||||
os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
|
||||
os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
|
||||
]
|
||||
|
||||
need_build = False
|
||||
for f in _chk_output:
|
||||
if not os.path.exists(f):
|
||||
need_build = True
|
||||
break
|
||||
|
||||
if not need_build:
|
||||
cc.n('build openssl static library from source code... ', end='')
|
||||
cc.n('prepare OpenSSL pre-built package ... ', end='')
|
||||
if os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
cc.w('already exists, skip.')
|
||||
return
|
||||
cc.v('')
|
||||
|
||||
cc.n('prepare openssl source code...')
|
||||
_alt_ver = '_'.join(env.ver_ossl.split('.'))
|
||||
if not os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
|
||||
os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
|
||||
if not os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
raise RuntimeError('can not prepare openssl source code.')
|
||||
else:
|
||||
cc.w('already exists, skip.')
|
||||
|
||||
os.chdir(self.OPENSSL_PATH_SRC)
|
||||
os.system('""{}" Configure VC-WIN32"'.format(env.perl))
|
||||
os.system(r'ms\do_nasm')
|
||||
# for vs2015
|
||||
# utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
|
||||
# for vs2017 community
|
||||
utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
|
||||
file_name = 'Win32OpenSSL-{}.msi'.format(_alt_ver)
|
||||
installer = os.path.join(PATH_DOWNLOAD, file_name)
|
||||
|
||||
for f in _chk_output:
|
||||
if not os.path.exists(f):
|
||||
raise RuntimeError('build openssl static library from source code failed.')
|
||||
if not os.path.exists(installer):
|
||||
if not utils.download_file('openssl installer', 'http://slproweb.com/download/{}'.format(filename), PATH_DOWNLOAD, file_name):
|
||||
cc.e('can not download pre-built installer of OpenSSL.')
|
||||
return
|
||||
|
||||
utils.ensure_file_exists(installer)
|
||||
|
||||
cc.w('On Windows, we use pre-built package of OpenSSL.')
|
||||
cc.w('The installer have been downloaded at "{}".'.format(installer))
|
||||
cc.w('please install OpenSSL into "{}".'.format(self.OPENSSL_PATH_SRC))
|
||||
cc.w('\nOnce the OpenSSL installed, press Enter to continue or Q to quit...', end='')
|
||||
try:
|
||||
x = env.input()
|
||||
except EOFError:
|
||||
x = 'q'
|
||||
if x == 'q':
|
||||
return
|
||||
|
||||
|
||||
# cc.n('build openssl static library from source code... ')
|
||||
|
||||
# if not super()._build_openssl(file_name):
|
||||
# return
|
||||
|
||||
# _chk_output = [
|
||||
# os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
|
||||
# os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
|
||||
# os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
|
||||
# ]
|
||||
|
||||
# need_build = False
|
||||
# for f in _chk_output:
|
||||
# if not os.path.exists(f):
|
||||
# need_build = True
|
||||
# break
|
||||
|
||||
# if not need_build:
|
||||
# cc.n('build openssl static library from source code... ', end='')
|
||||
# cc.w('already exists, skip.')
|
||||
# return
|
||||
# cc.v('')
|
||||
|
||||
# cc.n('prepare openssl source code...')
|
||||
# _alt_ver = '_'.join(env.ver_ossl.split('.'))
|
||||
# if not os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
# utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
|
||||
# os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
|
||||
# if not os.path.exists(self.OPENSSL_PATH_SRC):
|
||||
# raise RuntimeError('can not prepare openssl source code.')
|
||||
# else:
|
||||
# cc.w('already exists, skip.')
|
||||
|
||||
# os.chdir(self.OPENSSL_PATH_SRC)
|
||||
# os.system('""{}" Configure VC-WIN32"'.format(env.perl))
|
||||
# os.system(r'ms\do_nasm')
|
||||
# # for vs2015
|
||||
# # utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
|
||||
# # for vs2017 community
|
||||
# utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
|
||||
|
||||
# for f in _chk_output:
|
||||
# if not os.path.exists(f):
|
||||
# raise RuntimeError('build openssl static library from source code failed.')
|
||||
|
||||
def _build_libssh(self, file_name):
|
||||
cc.n('build libssh static library from source code... ', end='')
|
||||
if not self._download_libssh(file_name):
|
||||
return
|
||||
cc.n('build libssh library from source code... ', end='')
|
||||
|
||||
if not os.path.exists(self.LIBSSH_PATH_SRC):
|
||||
cc.v('')
|
||||
|
@ -210,7 +275,7 @@ class BuilderWin(BuilderBase):
|
|||
|
||||
cc.i('build libssh...')
|
||||
sln_file = os.path.join(self.LIBSSH_PATH_SRC, 'build', 'libssh.sln')
|
||||
utils.msvc_build(sln_file, 'ssh_shared', ctx.target_path, 'win32', False)
|
||||
utils.msvc_build(sln_file, 'ssh', ctx.target_path, 'win32', False)
|
||||
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.lib'))
|
||||
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.dll'))
|
||||
utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path), os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path), 'ssh.lib')
|
||||
|
@ -218,7 +283,53 @@ class BuilderWin(BuilderBase):
|
|||
utils.ensure_file_exists(out_file_lib)
|
||||
utils.ensure_file_exists(out_file_dll)
|
||||
|
||||
def _build_zlib(self, file_name):
|
||||
if not self._download_zlib(file_name):
|
||||
return
|
||||
cc.n('build zlib library from source code... ', end='')
|
||||
|
||||
if not os.path.exists(self.ZLIB_PATH_SRC):
|
||||
cc.v('')
|
||||
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
|
||||
os.rename(os.path.join(PATH_EXTERNAL, 'zlib-{}'.format(env.ver_zlib)), self.ZLIB_PATH_SRC)
|
||||
|
||||
if ctx.target_path == 'debug':
|
||||
olib = 'zlibd.lib'
|
||||
odll = 'zlibd.dll'
|
||||
else:
|
||||
olib = 'zlib.lib'
|
||||
odll = 'zlib.dll'
|
||||
out_file_lib = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, olib)
|
||||
out_file_dll = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, odll)
|
||||
|
||||
if os.path.exists(out_file_lib) and os.path.exists(out_file_dll):
|
||||
cc.w('already exists, skip.')
|
||||
return
|
||||
cc.v('')
|
||||
|
||||
cc.w('On Windows, when build zlib, need you use cmake-gui.exe to generate solution file')
|
||||
cc.w('for Visual Studio 2017. Visit https://docs.tp4a.com for more details.')
|
||||
cc.w('\nOnce the zlib.sln generated, press Enter to continue or Q to quit...', end='')
|
||||
try:
|
||||
x = env.input()
|
||||
except EOFError:
|
||||
x = 'q'
|
||||
if x == 'q':
|
||||
return
|
||||
|
||||
cc.i('build zlib...')
|
||||
sln_file = os.path.join(self.ZLIB_PATH_SRC, 'build', 'zlib.sln')
|
||||
utils.msvc_build(sln_file, 'zlib', ctx.target_path, 'win32', False)
|
||||
# utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.lib'))
|
||||
# utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.dll'))
|
||||
# utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.lib')
|
||||
# utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.dll')
|
||||
utils.ensure_file_exists(out_file_lib)
|
||||
utils.ensure_file_exists(out_file_dll)
|
||||
|
||||
def _build_jsoncpp(self, file_name):
|
||||
if not self._download_jsoncpp(file_name):
|
||||
return
|
||||
cc.n('prepare jsoncpp source code... ', end='')
|
||||
if not os.path.exists(self.JSONCPP_PATH_SRC):
|
||||
cc.v('')
|
||||
|
@ -228,6 +339,8 @@ class BuilderWin(BuilderBase):
|
|||
cc.w('already exists, skip.')
|
||||
|
||||
def _build_mongoose(self, file_name):
|
||||
if not self._download_mongoose(file_name):
|
||||
return
|
||||
cc.n('prepare mongoose source code... ', end='')
|
||||
if not os.path.exists(self.MONGOOSE_PATH_SRC):
|
||||
cc.v('')
|
||||
|
@ -237,6 +350,8 @@ class BuilderWin(BuilderBase):
|
|||
cc.w('already exists, skip.')
|
||||
|
||||
def _build_mbedtls(self, file_name):
|
||||
if not self._download_mbedtls(file_name):
|
||||
return
|
||||
cc.n('prepare mbedtls source code... ', end='')
|
||||
if not os.path.exists(self.MBEDTLS_PATH_SRC):
|
||||
cc.v('')
|
||||
|
@ -254,6 +369,8 @@ class BuilderWin(BuilderBase):
|
|||
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library'), os.path.join(self.MBEDTLS_PATH_SRC, 'library'), 'rsa.c')
|
||||
|
||||
def _build_libuv(self, file_name):
|
||||
if not self._download_libuv(file_name):
|
||||
return
|
||||
cc.n('prepare libuv source code... ', end='')
|
||||
if not os.path.exists(self.LIBUV_PATH_SRC):
|
||||
cc.v('')
|
||||
|
@ -674,6 +791,7 @@ def main():
|
|||
builder.build_openssl()
|
||||
builder.build_libuv()
|
||||
builder.build_mbedtls()
|
||||
builder.build_zlib()
|
||||
builder.build_libssh()
|
||||
|
||||
builder.fix_output()
|
||||
|
|
|
@ -148,6 +148,11 @@ class Env(object):
|
|||
if warn_miss_tool:
|
||||
cc.w(' - can not locate `nsis`, so I can not make installer.')
|
||||
|
||||
if 'qt' in _tmp:
|
||||
self.qt = _tmp['qt']
|
||||
else:
|
||||
self.qt = None
|
||||
|
||||
elif self.is_linux or self.is_macos:
|
||||
if 'cmake' in _tmp:
|
||||
self.cmake = _tmp['cmake']
|
||||
|
@ -178,6 +183,10 @@ class Env(object):
|
|||
self.ver_ossl = _v_openssl[0].strip()
|
||||
self.ver_ossl_number = _v_openssl[1].strip()
|
||||
|
||||
_v_zlib = _tmp['zlib'].split(',')
|
||||
self.ver_zlib = _v_zlib[0].strip()
|
||||
self.ver_zlib_number = _v_zlib[1].strip()
|
||||
|
||||
self.ver_libuv = _tmp['libuv']
|
||||
self.ver_mbedtls = _tmp['mbedtls']
|
||||
# self.ver_sqlite = _tmp['sqlite']
|
||||
|
|
|
@ -320,6 +320,21 @@ def msvc_build(sln_file, proj_name, target, platform, force_rebuild):
|
|||
raise RuntimeError('build MSVC project `{}` failed.'.format(proj_name))
|
||||
|
||||
|
||||
def qt_build_win(prj_path, prj_name, bit_path, target_path):
|
||||
cc.n(env.visual_studio_path)
|
||||
if env.qt is None:
|
||||
raise RuntimeError('where is `qt`?')
|
||||
|
||||
if env.is_win:
|
||||
tmp_path = os.path.join(env.root_path, 'out', '_tmp_', prj_name, bit_path)
|
||||
# C:\Windows\System32\cmd.exe /A /Q /K C:\Qt\Qt5.12.0\5.12.0\msvc2017\bin\qtenv2.bat
|
||||
cmd = 'C:\\Windows\\System32\\cmd.exe /A /Q /C ""{}\qt-helper.bat" "{}\\bin\\qtenv2.bat" "{}VC\\Auxiliary\\Build\\vcvarsall.bat" {} "{}" "{}" {}"'.format(env.build_path, env.qt, env.visual_studio_path, bit_path, tmp_path, prj_path, target_path)
|
||||
ret, _ = sys_exec(cmd, direct_output=True)
|
||||
if ret != 0:
|
||||
raise RuntimeError('build XCode project `{}` failed.'.format(proj_name))
|
||||
|
||||
|
||||
|
||||
def xcode_build(proj_file, proj_name, target, force_rebuild):
|
||||
if force_rebuild:
|
||||
cmd = 'xcodebuild -project "{}" -target {} -configuration {} clean'.format(proj_file, proj_name, target)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf8 -*-
|
||||
VER_TP_SERVER = "3.3.1"
|
||||
VER_TP_ASSIST = "3.3.1"
|
||||
VER_TP_SERVER = "3.5.1"
|
||||
VER_TP_ASSIST = "3.5.1"
|
||||
|
|
|
@ -71,12 +71,18 @@
|
|||
"rdp" : {
|
||||
"available" : [
|
||||
{
|
||||
"app" : "mstsc.exe",
|
||||
"cmdline" : "\"{tmp_rdp_file}\"",
|
||||
"display" : "微软RDP客户端(系统自带)",
|
||||
"name" : "mstsc"
|
||||
},
|
||||
{
|
||||
"app" : "{assist_tools_path}\\tprdp\\tprdp-client.exe",
|
||||
"cmdline" : "/v:{host_ip}:{host_port} /u:{user_name} /t:\"TP#{real_ip}\"",
|
||||
"display" : "FreeRDP(内置)",
|
||||
"name" : "freerdp"
|
||||
}
|
||||
],
|
||||
"selected" : "freerdp"
|
||||
"selected" : "mstsc"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,718 @@
|
|||
#include "bar.h"
|
||||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
#include "mainwindow.h"
|
||||
|
||||
|
||||
#define FONT_SIZE_DEFAULT 12
|
||||
#define FONT_SIZE_TIME 14
|
||||
#define TEXT_COLOR QColor(255,255,255,153)
|
||||
#define SPEED_BTN_WIDTH 42
|
||||
#define CHKBOX_RIGHT_PADDING 6
|
||||
#define PADDING_TIME_PROGRESS_BAR 10
|
||||
#define SPEED_BTN_PADDING_TOP 8
|
||||
#define SPEED_BTN_PADDING_RIGHT 8
|
||||
#define SKIP_PADDING_TOP 10
|
||||
|
||||
#define BAR_ALIGN_TOP 10
|
||||
#define BAR_PADDING_TOP 18
|
||||
#define BAR_PADDING_LEFT 15
|
||||
#define BAR_PADDING_RIGHT 15
|
||||
|
||||
typedef struct RES_MAP {
|
||||
RES_ID id;
|
||||
const char* name;
|
||||
}RES_MAP;
|
||||
|
||||
static RES_MAP img_res[res__max] = {
|
||||
{res_bg_left, "bg-left"},
|
||||
{res_bg_mid, "bg-mid"},
|
||||
{res_bg_right, "bg-right"},
|
||||
{res_btn_normal_left, "btn-normal-left"},
|
||||
{res_btn_normal_mid, "btn-normal-mid"},
|
||||
{res_btn_normal_right, "btn-normal-right"},
|
||||
{res_btn_sel_left, "btn-sel-left"},
|
||||
{res_btn_sel_mid, "btn-sel-mid"},
|
||||
{res_btn_sel_right, "btn-sel-right"},
|
||||
{res_btn_hover_left, "btn-hover-left"},
|
||||
{res_btn_hover_mid, "btn-hover-mid"},
|
||||
{res_btn_hover_right, "btn-hover-right"},
|
||||
|
||||
{res_prgbarh_left, "prgbarh-left"},
|
||||
{res_prgbarh_mid, "prgbarh-mid"},
|
||||
{res_prgbar_mid, "prgbar-mid"},
|
||||
{res_prgbar_right, "prgbar-right"},
|
||||
|
||||
{res_chkbox_normal, "chkbox-normal"},
|
||||
{res_chkbox_hover, "chkbox-hover"},
|
||||
{res_chkbox_sel_normal, "chkbox-sel-normal"},
|
||||
{res_chkbox_sel_hover, "chkbox-sel-hover"},
|
||||
};
|
||||
|
||||
typedef struct SPEED_MAP {
|
||||
int id;
|
||||
const char* title;
|
||||
}SPEED_MAP;
|
||||
|
||||
static SPEED_MAP speed[speed_count] = {
|
||||
{speed_1x, "1x"},
|
||||
{speed_2x, "2x"},
|
||||
{speed_4x, "4x"},
|
||||
{speed_8x, "8x"},
|
||||
};
|
||||
|
||||
static inline int min(int a, int b){
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static inline int max(int a, int b){
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
Bar::Bar() {
|
||||
m_img_ready = false;
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
m_str_total_time = "00:00";
|
||||
m_str_passed_time = "00:00";
|
||||
m_str_passed_time_last_draw = "--:--";
|
||||
|
||||
m_percent = 0;
|
||||
m_percent_last_draw = -1;
|
||||
|
||||
m_play_hover = false;
|
||||
m_playing = true; // false=paused
|
||||
m_speed_selected = speed_1x;
|
||||
m_speed_hover = speed_count; // speed_count=no-hover
|
||||
m_skip_selected = false;
|
||||
m_skip_hover = false;
|
||||
m_progress_hover = false;
|
||||
m_progress_pressed = false;
|
||||
|
||||
m_resume_ms = 0;
|
||||
}
|
||||
|
||||
Bar::~Bar() {
|
||||
|
||||
}
|
||||
|
||||
bool Bar::init(MainWindow* owner) {
|
||||
m_owner = owner;
|
||||
|
||||
// 加载所需的图像资源
|
||||
int i = 0;
|
||||
for(i = 0; i < res__max; ++i) {
|
||||
QString name;
|
||||
name.sprintf(":/tp-player/res/bar/%s.png", img_res[i].name);
|
||||
if(!m_res[i].load(name))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 无需合成的图像
|
||||
if(!m_img_btn_play[play_running][widget_normal].load(":/tp-player/res/bar/play-normal.png")
|
||||
|| !m_img_btn_play[play_running][widget_hover].load(":/tp-player/res/bar/play-hover.png")
|
||||
|| !m_img_btn_play[play_paused][widget_normal].load(":/tp-player/res/bar/pause-normal.png")
|
||||
|| !m_img_btn_play[play_paused][widget_hover].load(":/tp-player/res/bar/pause-hover.png")
|
||||
|| !m_img_progress_pointer[widget_normal].load(":/tp-player/res/bar/prgpt-normal.png")
|
||||
|| !m_img_progress_pointer[widget_hover].load(":/tp-player/res/bar/prgpt-hover.png")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_height = m_res[res_bg_left].height();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bar::start(uint32_t total_ms, int width) {
|
||||
bool is_first_start = (m_width == 0);
|
||||
m_width = width;
|
||||
|
||||
m_total_ms = total_ms;
|
||||
_ms_to_str(total_ms, m_str_total_time);
|
||||
|
||||
|
||||
// 首次播放时,调整位置左右居中,距窗口顶部10点处。
|
||||
if(is_first_start) {
|
||||
_init_imgages();
|
||||
QRect rc = m_owner->rect();
|
||||
m_rc = QRect(0, 0, m_width, m_height);
|
||||
m_rc.moveTo((rc.width() - m_width)/2, BAR_ALIGN_TOP);
|
||||
}
|
||||
}
|
||||
|
||||
void Bar::end() {
|
||||
if(m_played_ms != m_total_ms)
|
||||
update_passed_time(m_total_ms);
|
||||
|
||||
m_playing = false;
|
||||
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
|
||||
}
|
||||
|
||||
void Bar::_init_imgages() {
|
||||
m_img_bg = QPixmap(m_width, m_height);
|
||||
m_img_bg.fill(Qt::transparent);//用透明色填充
|
||||
QPainter pp(&m_img_bg);
|
||||
QFont font = pp.font();
|
||||
|
||||
// 合成背景图像
|
||||
{
|
||||
pp.drawPixmap(0, 0, m_res[res_bg_left].width(), m_res[res_bg_left].height(), m_res[res_bg_left]);
|
||||
pp.drawPixmap(m_res[res_bg_left].width(), 0, m_width - m_res[res_bg_left].width() - m_res[res_bg_right].width(), m_height, m_res[res_bg_mid]);
|
||||
pp.drawPixmap(m_width-m_res[res_bg_right].width(), 0, m_res[res_bg_right].width(), m_height, m_res[res_bg_right]);
|
||||
}
|
||||
|
||||
{
|
||||
m_rc_btn_play = QRect(BAR_PADDING_LEFT, (m_height - m_img_btn_play[play_running][widget_normal].height())/2 , m_img_btn_play[play_running][widget_normal].width(), m_img_btn_play[play_running][widget_normal].height());
|
||||
}
|
||||
|
||||
// 合成速度按钮
|
||||
{
|
||||
int w = SPEED_BTN_WIDTH, h = m_res[res_btn_normal_left].height();
|
||||
QRect rc(0, 0, w, h);
|
||||
QPixmap btn[btnspd_state_count];
|
||||
|
||||
// 未选中状态
|
||||
btn[btnspd_normal] = QPixmap(w, h);
|
||||
btn[btnspd_normal].fill(Qt::transparent);//用透明色填充
|
||||
QPainter pn(&btn[btnspd_normal]);
|
||||
pn.drawPixmap(0, 0, m_res[res_btn_normal_left].width(), m_res[res_btn_normal_left].height(), m_res[res_btn_normal_left]);
|
||||
pn.drawPixmap(m_res[res_btn_normal_left].width(), 0, w - m_res[res_btn_normal_left].width() - m_res[res_btn_normal_right].width(), h, m_res[res_btn_normal_mid]);
|
||||
pn.drawPixmap(w-m_res[res_btn_normal_right].width(), 0, m_res[res_btn_normal_right].width(), h, m_res[res_btn_normal_right]);
|
||||
// 选中状态
|
||||
btn[btnspd_sel] = QPixmap(w, h);
|
||||
btn[btnspd_sel].fill(Qt::transparent);//用透明色填充
|
||||
QPainter ps(&btn[btnspd_sel]);
|
||||
ps.drawPixmap(0, 0, m_res[res_btn_sel_left].width(), m_res[res_btn_sel_left].height(), m_res[res_btn_sel_left]);
|
||||
ps.drawPixmap(m_res[res_btn_sel_left].width(), 0, w - m_res[res_btn_sel_left].width() - m_res[res_btn_sel_right].width(), h, m_res[res_btn_sel_mid]);
|
||||
ps.drawPixmap(w-m_res[res_btn_sel_right].width(), 0, m_res[res_btn_sel_right].width(), h, m_res[res_btn_sel_right]);
|
||||
// 鼠标滑过状态
|
||||
btn[btnspd_hover] = QPixmap(w, h);
|
||||
btn[btnspd_hover].fill(Qt::transparent);//用透明色填充
|
||||
QPainter ph(&btn[btnspd_hover]);
|
||||
ph.drawPixmap(0, 0, m_res[res_btn_hover_left].width(), m_res[res_btn_hover_left].height(), m_res[res_btn_hover_left]);
|
||||
ph.drawPixmap(m_res[res_btn_hover_left].width(), 0, w - m_res[res_btn_hover_left].width() - m_res[res_btn_hover_right].width(), h, m_res[res_btn_hover_mid]);
|
||||
ph.drawPixmap(w-m_res[res_btn_hover_right].width(), 0, m_res[res_btn_hover_right].width(), h, m_res[res_btn_hover_right]);
|
||||
|
||||
for(int i = 0; i < btnspd_state_count; ++i) {
|
||||
for(int j = 0; j < speed_count; ++j) {
|
||||
m_img_btn_speed[j][i] = QPixmap(w, h);
|
||||
m_img_btn_speed[j][i].fill(Qt::transparent);
|
||||
QPainter ps(&m_img_btn_speed[j][i]);
|
||||
ps.setPen(TEXT_COLOR);
|
||||
QFont font = ps.font();
|
||||
font.setFamily("consolas");
|
||||
font.setPixelSize(FONT_SIZE_DEFAULT);
|
||||
ps.setFont(font);
|
||||
ps.drawPixmap(0, 0, w, h, btn[i]);
|
||||
ps.drawText(rc, Qt::AlignCenter, speed[j].title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 合成跳过无操作选项
|
||||
{
|
||||
// 计算显示跳过无操作选项字符串的宽高
|
||||
font.setFamily("微软雅黑");
|
||||
font.setBold(false);
|
||||
font.setPixelSize(FONT_SIZE_DEFAULT);
|
||||
pp.setFont(font);
|
||||
QFontMetrics fm = pp.fontMetrics();
|
||||
|
||||
{
|
||||
int h = fm.height();
|
||||
if(h < m_res[res_chkbox_normal].height())
|
||||
h = m_res[res_chkbox_normal].height();
|
||||
m_rc_skip = QRect(0, 0, fm.width(LOCAL8BIT("无操作则跳过")) + CHKBOX_RIGHT_PADDING + m_res[res_chkbox_normal].width(), h);
|
||||
}
|
||||
|
||||
int w = m_rc_skip.width();
|
||||
int h = m_rc_skip.height();
|
||||
int chkbox_top = (m_rc_skip.height() - m_res[res_chkbox_normal].height()) / 2;
|
||||
int text_left = m_res[res_chkbox_normal].width() + CHKBOX_RIGHT_PADDING;
|
||||
int text_top = (m_rc_skip.height() - fm.height()) / 2;
|
||||
|
||||
for(int i = 0; i < chkbox_state_count; ++i) {
|
||||
for(int j = 0; j < widget_state_count; ++j) {
|
||||
m_img_skip[i][j] = QPixmap(w,h);
|
||||
m_img_skip[i][j].fill(Qt::transparent);
|
||||
QPainter ps(&m_img_skip[i][j]);
|
||||
ps.setPen(TEXT_COLOR);
|
||||
QFont font = ps.font();
|
||||
font.setFamily("微软雅黑");
|
||||
font.setPixelSize(FONT_SIZE_DEFAULT);
|
||||
ps.setFont(font);
|
||||
|
||||
QPixmap* img = nullptr;
|
||||
if(i == chkbox_normal && j == widget_normal)
|
||||
img = &m_res[res_chkbox_normal];
|
||||
else if(i == chkbox_normal && j == widget_hover)
|
||||
img = &m_res[res_chkbox_hover];
|
||||
else if(i == chkbox_selected && j == widget_normal)
|
||||
img = &m_res[res_chkbox_sel_normal];
|
||||
else if(i == chkbox_selected && j == widget_hover)
|
||||
img = &m_res[res_chkbox_sel_hover];
|
||||
|
||||
if(img == nullptr) {
|
||||
qDebug("ERROR: can not load image for check-box.");
|
||||
img = &m_res[res_chkbox_normal];
|
||||
}
|
||||
ps.drawPixmap(0, chkbox_top, img->width(), img->height(), *img);
|
||||
ps.drawText(QRect(text_left, text_top, w-text_left, h-text_top), Qt::AlignCenter, LOCAL8BIT("无操作则跳过"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 定位进度条
|
||||
{
|
||||
// 计算显示时间所需的宽高
|
||||
font.setFamily("consolas");
|
||||
font.setBold(true);
|
||||
font.setPixelSize(FONT_SIZE_TIME);
|
||||
pp.setFont(font);
|
||||
{
|
||||
QFontMetrics fm = pp.fontMetrics();
|
||||
m_rc_time_passed = QRect(0, 0, fm.width("00:00:00"), fm.height());
|
||||
m_rc_time_total = m_rc_time_passed;
|
||||
}
|
||||
|
||||
m_img_time_total = QPixmap(m_rc_time_total.width(), m_rc_time_total.height());
|
||||
m_img_time_total.fill(Qt::transparent);
|
||||
QPainter pp(&m_img_time_total);
|
||||
pp.setPen(TEXT_COLOR);
|
||||
QFont font = pp.font();
|
||||
font.setFamily("consolas");
|
||||
font.setBold(true);
|
||||
font.setPixelSize(FONT_SIZE_TIME);
|
||||
pp.setFont(font);
|
||||
pp.drawText(m_rc_time_total, Qt::AlignLeft, m_str_total_time);
|
||||
|
||||
// 定位时间字符串的位置
|
||||
m_rc_time_passed.moveTo(BAR_PADDING_LEFT+m_img_btn_play[play_running][widget_normal].width()+PADDING_TIME_PROGRESS_BAR, BAR_PADDING_TOP);
|
||||
m_rc_time_total.moveTo(m_width - BAR_PADDING_RIGHT - m_rc_time_total.width(), BAR_PADDING_TOP);
|
||||
|
||||
int prog_width = m_rc_time_total.left() - PADDING_TIME_PROGRESS_BAR - PADDING_TIME_PROGRESS_BAR - m_rc_time_passed.right();
|
||||
int prog_height = max(m_res[res_prgbarh_left].height(), m_img_progress_pointer->height());
|
||||
m_rc_progress = QRect(0, 0, prog_width, prog_height);
|
||||
m_rc_progress.moveTo(m_rc_time_passed.right() + PADDING_TIME_PROGRESS_BAR, m_rc_time_passed.top() + (m_rc_time_passed.height() - prog_height)/2);
|
||||
}
|
||||
|
||||
|
||||
// 定位速度按钮
|
||||
{
|
||||
int left = m_rc_time_passed.right() + PADDING_TIME_PROGRESS_BAR;
|
||||
int top = m_rc_time_passed.bottom() + SPEED_BTN_PADDING_TOP;
|
||||
for(int i = 0; i < speed_count; i++) {
|
||||
m_rc_btn_speed[i] = QRect(left, top, m_img_btn_speed[i][widget_normal].width(), m_img_btn_speed[i][widget_normal].height());
|
||||
left += m_img_btn_speed[i][widget_normal].width() + SPEED_BTN_PADDING_RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// 定位跳过选项
|
||||
{
|
||||
int left = m_rc_time_total.left() - m_rc_skip.width() - PADDING_TIME_PROGRESS_BAR;
|
||||
int top = m_rc_time_passed.bottom() + SKIP_PADDING_TOP;//m_rc_btn_speed[0].top() + (m_rc_btn_speed[0].height() - m_rc_skip.height())/2;
|
||||
m_rc_skip.moveTo(left, top);
|
||||
}
|
||||
|
||||
m_img_ready = true;
|
||||
}
|
||||
|
||||
void Bar::_ms_to_str(uint32_t ms, QString& str) {
|
||||
int h = 0, m = 0, s = 0;
|
||||
s = ms / 1000;
|
||||
if(ms % 1000 > 500)
|
||||
s += 1;
|
||||
|
||||
h = s / 3600;
|
||||
s = s % 3600;
|
||||
m = s / 60;
|
||||
s = s % 60;
|
||||
|
||||
if(h > 0)
|
||||
str.sprintf("%02d:%02d:%02d", h, m, s);
|
||||
else
|
||||
str.sprintf("%02d:%02d", m, s);
|
||||
}
|
||||
|
||||
void Bar::update_passed_time(uint32_t ms) {
|
||||
QString str_passed;
|
||||
_ms_to_str(ms, str_passed);
|
||||
|
||||
if(m_str_passed_time != str_passed)
|
||||
{
|
||||
m_str_passed_time = str_passed;
|
||||
m_owner->update(m_rc.left()+m_rc_time_passed.left(), m_rc.top()+m_rc_time_passed.top(), m_rc_time_passed.width(), m_rc_time_passed.height());
|
||||
}
|
||||
|
||||
int percent = 0;
|
||||
if(ms >= m_total_ms) {
|
||||
percent = 100;
|
||||
m_played_ms = m_total_ms;
|
||||
}
|
||||
else {
|
||||
m_played_ms = ms;
|
||||
//percent = (int)(((double)m_played_ms / (double)m_total_ms) * 100);
|
||||
percent = m_played_ms * 100 / m_total_ms;
|
||||
}
|
||||
|
||||
if(percent != m_percent) {
|
||||
m_percent = percent;
|
||||
m_owner->update(m_rc.left()+m_rc_progress.left(), m_rc.top()+m_rc_progress.top(), m_rc_progress.width(), m_rc_progress.height());
|
||||
}
|
||||
}
|
||||
|
||||
void Bar::onMouseMove(int x, int y) {
|
||||
// 映射鼠标坐标点到本浮动窗内部的相对位置
|
||||
QPoint pt(x-m_rc.left(), y-m_rc.top());
|
||||
|
||||
if(m_progress_pressed) {
|
||||
// 重新设置进度条指示器位置
|
||||
int percent = 0;
|
||||
|
||||
if(pt.x() < m_rc_progress.left()) {
|
||||
percent = 0;
|
||||
m_resume_ms = 1;
|
||||
}
|
||||
else if(pt.x() > m_rc_progress.right()) {
|
||||
percent = 100;
|
||||
m_resume_ms = m_total_ms;
|
||||
}
|
||||
else {
|
||||
percent = (pt.x() + m_img_progress_pointer[widget_normal].width()/2 - m_rc_progress.left()) * 100 / m_rc_progress.width();
|
||||
m_resume_ms = m_total_ms * percent / 100;
|
||||
}
|
||||
update_passed_time(m_resume_ms);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool play_hover = m_rc_btn_play.contains(pt);
|
||||
if(play_hover != m_play_hover) {
|
||||
m_play_hover = play_hover;
|
||||
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
|
||||
}
|
||||
if(play_hover)
|
||||
return;
|
||||
|
||||
int speed_hover = speed_count;
|
||||
for(int i = 0; i < speed_count; ++i) {
|
||||
if(m_rc_btn_speed[i].contains(pt)) {
|
||||
speed_hover = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(m_speed_hover != speed_hover) {
|
||||
if(m_speed_hover != speed_count) {
|
||||
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
|
||||
}
|
||||
m_speed_hover = speed_hover;
|
||||
if(m_speed_hover != speed_count) {
|
||||
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
|
||||
}
|
||||
}
|
||||
|
||||
bool skip_hover = m_rc_skip.contains(pt);
|
||||
if(skip_hover != m_skip_hover) {
|
||||
m_skip_hover = skip_hover;
|
||||
m_owner->update(m_rc.left()+m_rc_skip.left(), m_rc.top()+m_rc_skip.top(), m_rc_skip.width(), m_rc_skip.height());
|
||||
}
|
||||
if(skip_hover)
|
||||
return;
|
||||
}
|
||||
|
||||
void Bar::onMousePress(int x, int y, Qt::MouseButton button) {
|
||||
// 我们只关心左键按下
|
||||
if(button != Qt::LeftButton)
|
||||
return;
|
||||
|
||||
// 映射鼠标坐标点到本浮动窗内部的相对位置
|
||||
QPoint pt(x-m_rc.left(), y-m_rc.top());
|
||||
|
||||
if(m_rc_btn_play.contains(pt)) {
|
||||
if(m_playing)
|
||||
m_owner->pause();
|
||||
else
|
||||
m_owner->resume(false, 0);
|
||||
|
||||
m_playing = !m_playing;
|
||||
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int speed_sel = speed_count;
|
||||
for(int i = 0; i < speed_count; ++i) {
|
||||
if(m_rc_btn_speed[i].contains(pt)) {
|
||||
speed_sel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(m_speed_selected != speed_sel && speed_sel != speed_count) {
|
||||
int old_sel = m_speed_selected;
|
||||
m_speed_selected = speed_sel;
|
||||
m_owner->set_speed(get_speed());
|
||||
m_owner->update(m_rc.left()+m_rc_btn_speed[old_sel].left(), m_rc.top()+m_rc_btn_speed[old_sel].top(), m_rc_btn_speed[old_sel].width(), m_rc_btn_speed[old_sel].height());
|
||||
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_rc_skip.contains(pt)) {
|
||||
m_skip_selected = !m_skip_selected;
|
||||
m_owner->set_skip(m_skip_selected);
|
||||
m_owner->update(m_rc.left()+m_rc_skip.left(), m_rc.top()+m_rc_skip.top(), m_rc_skip.width(), m_rc_skip.height());
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
if(m_rc_progress.contains(pt)) {
|
||||
m_progress_pressed = true;
|
||||
// TODO: 暂停播放,按比例计算出点击位置占整个录像时长的百分比,定位到此位置准备播放。
|
||||
// TODO: 如果点击的位置是进度条指示标志,则仅暂停播放
|
||||
m_owner->pause();
|
||||
m_playing = false;
|
||||
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
|
||||
|
||||
int percent = 0;
|
||||
|
||||
if(pt.x() < m_rc_progress.left()) {
|
||||
percent = 0;
|
||||
m_resume_ms = 0;
|
||||
}
|
||||
else if(pt.x() > m_rc_progress.right()) {
|
||||
percent = 100;
|
||||
m_resume_ms = m_total_ms;
|
||||
}
|
||||
else {
|
||||
percent = (pt.x() + m_img_progress_pointer[widget_normal].width()/2 - m_rc_progress.left()) * 100 / m_rc_progress.width();
|
||||
m_resume_ms = m_total_ms * percent / 100;
|
||||
}
|
||||
update_passed_time(m_resume_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void Bar::onMouseRelease(int, int, Qt::MouseButton button) {
|
||||
// 我们只关心左键释放
|
||||
if(button != Qt::LeftButton)
|
||||
return;
|
||||
if(m_progress_pressed) {
|
||||
m_progress_pressed = false;
|
||||
qDebug("resume at %dms.", m_resume_ms);
|
||||
m_owner->resume(true, m_resume_ms);
|
||||
m_playing = true;
|
||||
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
|
||||
}
|
||||
}
|
||||
|
||||
int Bar::get_speed() {
|
||||
switch (m_speed_selected) {
|
||||
case speed_1x:
|
||||
return 1;
|
||||
case speed_2x:
|
||||
return 2;
|
||||
case speed_4x:
|
||||
return 4;
|
||||
case speed_8x:
|
||||
return 8;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Bar::draw(QPainter& painter, const QRect& rc_draw){
|
||||
if(!m_width)
|
||||
return;
|
||||
if(!rc_draw.intersects(m_rc))
|
||||
return;
|
||||
|
||||
// 绘制背景
|
||||
{
|
||||
QRect rc(m_rc);
|
||||
//rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
|
||||
int from_x = max(rc_draw.left(), m_rc.left()) - m_rc.left();
|
||||
int from_y = max(rc_draw.top(), m_rc.top()) - m_rc.top();
|
||||
int w = min(m_rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
|
||||
int h = min(m_rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
|
||||
int to_x = m_rc.left() + from_x;
|
||||
int to_y = m_rc.top() + from_y;
|
||||
painter.drawPixmap(to_x, to_y, m_img_bg, from_x, from_y, w, h);
|
||||
}
|
||||
|
||||
// 绘制播放按钮
|
||||
{
|
||||
QRect rc(m_rc_btn_play);
|
||||
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
if(rc_draw.intersects(rc)) {
|
||||
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
|
||||
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
|
||||
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
|
||||
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
|
||||
int to_x = rc.left() + from_x;
|
||||
int to_y = rc.top() + from_y;
|
||||
if(m_playing){
|
||||
if(m_play_hover)
|
||||
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_paused][widget_hover], from_x, from_y, w, h);
|
||||
else
|
||||
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_paused][widget_normal], from_x, from_y, w, h);
|
||||
} else {
|
||||
if(m_play_hover)
|
||||
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_running][widget_hover], from_x, from_y, w, h);
|
||||
else
|
||||
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_running][widget_normal], from_x, from_y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制已播放时间
|
||||
{
|
||||
QRect rc(m_rc_time_passed);
|
||||
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
if(rc_draw.intersects(rc)) {
|
||||
if(m_str_passed_time != m_str_passed_time_last_draw) {
|
||||
m_img_time_passed = QPixmap(m_rc_time_passed.width(), m_rc_time_passed.height());
|
||||
m_img_time_passed.fill(Qt::transparent);
|
||||
QPainter pp(&m_img_time_passed);
|
||||
pp.setPen(TEXT_COLOR);
|
||||
QFont font = pp.font();
|
||||
font.setFamily("consolas");
|
||||
font.setBold(true);
|
||||
font.setPixelSize(FONT_SIZE_TIME);
|
||||
pp.setFont(font);
|
||||
pp.drawText(QRect(0,0,m_rc_time_passed.width(), m_rc_time_passed.height()), Qt::AlignRight, m_str_passed_time);
|
||||
|
||||
m_str_passed_time_last_draw = m_str_passed_time;
|
||||
}
|
||||
|
||||
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
|
||||
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
|
||||
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
|
||||
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
|
||||
int to_x = rc.left() + from_x;
|
||||
int to_y = rc.top() + from_y;
|
||||
painter.drawPixmap(to_x, to_y, m_img_time_passed, from_x, from_y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制总时间
|
||||
{
|
||||
QRect rc(m_rc_time_total);
|
||||
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
if(rc_draw.intersects(rc)) {
|
||||
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
|
||||
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
|
||||
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
|
||||
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
|
||||
int to_x = rc.left() + from_x;
|
||||
int to_y = rc.top() + from_y;
|
||||
painter.drawPixmap(to_x, to_y, m_img_time_total, from_x, from_y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制进度条
|
||||
{
|
||||
QRect rc(m_rc_progress);
|
||||
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
|
||||
if(rc_draw.intersects(rc)) {
|
||||
if(m_percent_last_draw != m_percent) {
|
||||
m_img_progress = QPixmap(m_rc_progress.width(), m_rc_progress.height());
|
||||
m_img_progress.fill(Qt::transparent);
|
||||
QPainter pp(&m_img_progress);
|
||||
|
||||
// 进度条
|
||||
int top = (rc.height() - m_res[res_prgbarh_left].height())/2;
|
||||
int passed_width = rc.width() * m_percent / 100; // 已经播放的进度条宽度
|
||||
int remain_width = rc.width() - passed_width; // 剩下未播放的进度条宽度
|
||||
|
||||
if(passed_width >= m_res[res_prgbarh_left].width())
|
||||
pp.drawPixmap(0, top , m_res[res_prgbarh_left].width(), m_res[res_prgbarh_left].height(), m_res[res_prgbarh_left]);
|
||||
if(passed_width > 0) {
|
||||
//pp.drawPixmap(m_res[res_pbh_left].width(), top, passed_width - m_res[res_pbh_left].width(), m_res[res_pbh_mid].height(), m_res[res_pbh_mid]);
|
||||
if(remain_width > m_res[res_prgbar_right].width())
|
||||
pp.drawPixmap(m_res[res_prgbarh_left].width(), top, passed_width - m_res[res_prgbarh_left].width(), m_res[res_prgbarh_mid].height(), m_res[res_prgbarh_mid]);
|
||||
else
|
||||
pp.drawPixmap(m_res[res_prgbarh_left].width(), top, passed_width - m_res[res_prgbarh_left].width() - m_res[res_prgbar_right].width(), m_res[res_prgbarh_mid].height(), m_res[res_prgbarh_mid]);
|
||||
}
|
||||
if(remain_width > 0)
|
||||
pp.drawPixmap(passed_width, top, remain_width - m_res[res_prgbar_right].width(), m_res[res_prgbar_mid].height(), m_res[res_prgbar_mid]);
|
||||
if(remain_width >= m_res[res_prgbar_right].width())
|
||||
pp.drawPixmap(rc.width() - m_res[res_prgbar_right].width(), top , m_res[res_prgbar_right].width(), m_res[res_prgbar_right].height(), m_res[res_prgbar_right]);
|
||||
|
||||
// 进度位置指示
|
||||
int left = passed_width - m_img_progress_pointer->width() / 2;
|
||||
if(left < 0)
|
||||
left = 0;
|
||||
if(left + m_img_progress_pointer->width() > rc.width())
|
||||
left = rc.width() - m_img_progress_pointer->width();
|
||||
top = (rc.height() - m_img_progress_pointer[widget_normal].height())/2;
|
||||
pp.drawPixmap(left, top , m_img_progress_pointer[widget_normal].width(), m_img_progress_pointer[widget_normal].height(), m_img_progress_pointer[widget_normal]);
|
||||
|
||||
m_percent_last_draw = m_percent;
|
||||
}
|
||||
|
||||
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
|
||||
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
|
||||
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
|
||||
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
|
||||
int to_x = rc.left() + from_x;
|
||||
int to_y = rc.top() + from_y;
|
||||
painter.drawPixmap(to_x, to_y, m_img_progress, from_x, from_y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制速度按钮
|
||||
{
|
||||
for(int i = 0; i < speed_count; i++) {
|
||||
QRect rc(m_rc_btn_speed[i]);
|
||||
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
if(rc_draw.intersects(rc)) {
|
||||
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
|
||||
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
|
||||
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
|
||||
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
|
||||
int to_x = rc.left() + from_x;
|
||||
int to_y = rc.top() + from_y;
|
||||
|
||||
if(m_speed_hover == i)
|
||||
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_hover], from_x, from_y, w, h);
|
||||
else if(m_speed_selected == i)
|
||||
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_sel], from_x, from_y, w, h);
|
||||
else
|
||||
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_normal], from_x, from_y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制跳过选项
|
||||
{
|
||||
QRect rc(m_rc_skip);
|
||||
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
|
||||
|
||||
// painter.fillRect(rc, QColor(255, 255, 255));
|
||||
|
||||
if(rc_draw.intersects(rc)) {
|
||||
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
|
||||
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
|
||||
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
|
||||
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
|
||||
int to_x = rc.left() + from_x;
|
||||
int to_y = rc.top() + from_y;
|
||||
//qDebug("skip (%d,%d), (%d,%d)/(%d,%d)", to_x, to_y, from_x, from_y, w, h);
|
||||
if(m_skip_selected) {
|
||||
if(m_skip_hover)
|
||||
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_selected][widget_hover], from_x, from_y, w, h);
|
||||
else
|
||||
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_selected][widget_normal], from_x, from_y, w, h);
|
||||
}
|
||||
else {
|
||||
if(m_skip_hover)
|
||||
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_normal][widget_hover], from_x, from_y, w, h);
|
||||
else
|
||||
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_normal][widget_normal], from_x, from_y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
#ifndef BAR_H
|
||||
#define BAR_H
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QWidget>
|
||||
|
||||
typedef enum {
|
||||
res_bg_left = 0, // 背景
|
||||
res_bg_mid,
|
||||
res_bg_right,
|
||||
res_btn_normal_left, // 按钮(速度选择),普通状态
|
||||
res_btn_normal_mid,
|
||||
res_btn_normal_right,
|
||||
res_btn_sel_left, // 按钮(速度选择),已选中
|
||||
res_btn_sel_mid,
|
||||
res_btn_sel_right,
|
||||
res_btn_hover_left, // 按钮(速度选择),鼠标滑过
|
||||
res_btn_hover_mid,
|
||||
res_btn_hover_right,
|
||||
|
||||
res_prgbarh_left, // 进度条(已经过)左侧
|
||||
res_prgbarh_mid, // 进度条(已经过)中间,拉伸填充
|
||||
res_prgbar_mid, // 进度条(未到达)中间,拉伸填充
|
||||
res_prgbar_right, // 进度条(未到达)右侧
|
||||
res_chkbox_normal, // 复选框
|
||||
res_chkbox_hover,
|
||||
res_chkbox_sel_normal,
|
||||
res_chkbox_sel_hover,
|
||||
|
||||
res__max
|
||||
}RES_ID;
|
||||
|
||||
//typedef enum {
|
||||
// widget_normal = 0,
|
||||
// widget_hover,
|
||||
// widget__max
|
||||
//}WIDGET_STAT;
|
||||
|
||||
#define widget_normal 0
|
||||
#define widget_hover 1
|
||||
#define widget_state_count 2
|
||||
|
||||
//typedef enum {
|
||||
// play_running = 0,
|
||||
// play_paused,
|
||||
// play__max
|
||||
//}PLAY_STAT;
|
||||
|
||||
#define play_running 0
|
||||
#define play_paused 1
|
||||
#define play_state_count 2
|
||||
|
||||
#define speed_1x 0
|
||||
#define speed_2x 1
|
||||
#define speed_4x 2
|
||||
#define speed_8x 3
|
||||
#define speed_count 4
|
||||
|
||||
#define btnspd_normal 0
|
||||
#define btnspd_sel 1
|
||||
#define btnspd_hover 2
|
||||
#define btnspd_state_count 3
|
||||
|
||||
#define chkbox_normal 0
|
||||
#define chkbox_selected 1
|
||||
#define chkbox_state_count 2
|
||||
|
||||
class MainWindow;
|
||||
|
||||
class Bar {
|
||||
public:
|
||||
Bar();
|
||||
~Bar();
|
||||
|
||||
bool init(MainWindow* owner);
|
||||
void start(uint32_t total_ms, int width);
|
||||
void end();
|
||||
void draw(QPainter& painter, const QRect& rc);
|
||||
void update_passed_time(uint32_t ms);
|
||||
|
||||
int get_speed();
|
||||
|
||||
QRect rc(){return m_rc;}
|
||||
|
||||
void onMouseMove(int x, int y);
|
||||
void onMousePress(int x, int y, Qt::MouseButton button);
|
||||
void onMouseRelease(int x, int y, Qt::MouseButton button);
|
||||
|
||||
private:
|
||||
void _init_imgages();
|
||||
void _ms_to_str(uint32_t ms, QString& str);
|
||||
|
||||
private:
|
||||
MainWindow* m_owner;
|
||||
|
||||
uint32_t m_total_ms; // 录像的总时长
|
||||
uint32_t m_played_ms; // 已经播放了的时长
|
||||
int m_percent; // 已经播放了的百分比(0~100)
|
||||
int m_percent_last_draw;
|
||||
QString m_str_total_time;
|
||||
QString m_str_passed_time;
|
||||
QString m_str_passed_time_last_draw;
|
||||
|
||||
bool m_img_ready;
|
||||
|
||||
// 从资源中加载的原始图像
|
||||
QPixmap m_res[res__max];
|
||||
QPixmap m_img_progress_pointer[widget_state_count];
|
||||
|
||||
int m_width;
|
||||
int m_height;
|
||||
// 此浮动窗相对于父窗口的坐标和大小
|
||||
QRect m_rc;
|
||||
|
||||
// 尺寸和定位(此浮动窗内部的相对坐标)
|
||||
QRect m_rc_btn_play;
|
||||
QRect m_rc_btn_speed[speed_count];
|
||||
QRect m_rc_time_passed;
|
||||
QRect m_rc_time_total;
|
||||
QRect m_rc_progress;
|
||||
QRect m_rc_skip;
|
||||
|
||||
// 画布,最终输出的图像
|
||||
//QPixmap m_canvas;
|
||||
|
||||
// 合成的图像
|
||||
QPixmap m_img_bg;
|
||||
QPixmap m_img_btn_play[play_state_count][widget_state_count];
|
||||
QPixmap m_img_btn_speed[speed_count][btnspd_state_count];
|
||||
QPixmap m_img_progress;
|
||||
QPixmap m_img_skip[chkbox_state_count][widget_state_count];
|
||||
QPixmap m_img_time_passed;
|
||||
QPixmap m_img_time_total;
|
||||
|
||||
// 各种状态
|
||||
bool m_playing; // 0=play, 2=pause
|
||||
bool m_play_hover;
|
||||
int m_speed_selected;
|
||||
int m_speed_hover; // speed__max=no-hover
|
||||
bool m_skip_selected;
|
||||
bool m_skip_hover;
|
||||
bool m_progress_hover;
|
||||
bool m_progress_pressed;
|
||||
|
||||
uint32_t m_resume_ms; // after drag progress-pointer, resume play from here.
|
||||
};
|
||||
|
||||
#endif // BAR_H
|
|
@ -0,0 +1,120 @@
|
|||
#include "downloader.h"
|
||||
#include "record_format.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <qelapsedtimer.h>
|
||||
|
||||
Downloader::Downloader() : QObject () {
|
||||
m_data = nullptr;
|
||||
m_reply = nullptr;
|
||||
m_result = false;
|
||||
}
|
||||
|
||||
Downloader::~Downloader() {
|
||||
}
|
||||
|
||||
bool Downloader::request(const QString& url, const QString& sid, const QString& filename) {
|
||||
return _request(url, sid, filename, nullptr);
|
||||
}
|
||||
|
||||
bool Downloader::request(const QString& url, const QString& sid, QByteArray* data) {
|
||||
QString fname;
|
||||
return _request(url, sid, fname, data);
|
||||
}
|
||||
|
||||
bool Downloader::_request(const QString& url, const QString& sid, const QString& filename, QByteArray* data) {
|
||||
if(filename.isEmpty() && data == nullptr)
|
||||
return false;
|
||||
if(!filename.isEmpty() && data != nullptr)
|
||||
return false;
|
||||
m_data = data;
|
||||
|
||||
if(!filename.isEmpty()) {
|
||||
m_file.setFileName(filename);
|
||||
if(!m_file.open(QIODevice::WriteOnly | QFile::Truncate)){
|
||||
qDebug("open file for write failed.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString cookie = QString("_sid=%1\r\n").arg(sid);
|
||||
|
||||
QNetworkRequest req;
|
||||
req.setUrl(QUrl(url));
|
||||
req.setRawHeader("Cookie", cookie.toLatin1());
|
||||
|
||||
QNetworkAccessManager* nam = new QNetworkAccessManager();
|
||||
QEventLoop eloop;
|
||||
m_reply = nam->get(req);
|
||||
|
||||
connect(m_reply, &QNetworkReply::finished, &eloop, &QEventLoop::quit);
|
||||
connect(m_reply, &QNetworkReply::finished, this, &Downloader::_on_finished);
|
||||
connect(m_reply, &QIODevice::readyRead, this, &Downloader::_on_data_ready);
|
||||
|
||||
// qDebug("before eventLoop.exec(%p)", &eloop);
|
||||
eloop.exec();
|
||||
// qDebug("after eventLoop.exec()");
|
||||
|
||||
disconnect(m_reply, &QNetworkReply::finished, &eloop, &QEventLoop::quit);
|
||||
disconnect(m_reply, &QNetworkReply::finished, this, &Downloader::_on_finished);
|
||||
disconnect(m_reply, &QIODevice::readyRead, this, &Downloader::_on_data_ready);
|
||||
|
||||
delete m_reply;
|
||||
m_reply = nullptr;
|
||||
delete nam;
|
||||
|
||||
qDebug("Downloader::_request() end.");
|
||||
return m_result;
|
||||
}
|
||||
|
||||
void Downloader::_on_data_ready() {
|
||||
// qDebug("Downloader::_on_data_ready(%p).", this);
|
||||
QNetworkReply *reply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
|
||||
if(m_data != nullptr) {
|
||||
m_data->push_back(reply->readAll());
|
||||
}
|
||||
else {
|
||||
m_file.write(reply->readAll());
|
||||
}
|
||||
}
|
||||
|
||||
void Downloader::abort() {
|
||||
if(m_reply) {
|
||||
qDebug("Downloader::abort().");
|
||||
m_reply->abort();
|
||||
}
|
||||
}
|
||||
|
||||
void Downloader::_on_finished() {
|
||||
// qDebug("Downloader::_on_finished(%p).", this);
|
||||
QNetworkReply *reply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
|
||||
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
// reply->abort() got "Operation canceled"
|
||||
//QString strError = reply->errorString();
|
||||
qDebug() << "ERROR:" << reply->errorString();
|
||||
if(m_data == nullptr) {
|
||||
m_file.flush();
|
||||
m_file.close();
|
||||
}
|
||||
m_result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_data != nullptr) {
|
||||
m_data->push_back(reply->readAll());
|
||||
}
|
||||
else {
|
||||
m_file.write(reply->readAll());
|
||||
m_file.flush();
|
||||
m_file.close();
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
m_result = true;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef DOWNLOADER_H
|
||||
#define DOWNLOADER_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
class Downloader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// 从url下载数据,写入到filename文件中,或放入data中。
|
||||
Downloader();
|
||||
~Downloader();
|
||||
|
||||
bool request(const QString& url, const QString& sid, const QString& filename);
|
||||
bool request(const QString& url, const QString& sid, QByteArray* data);
|
||||
void abort();
|
||||
|
||||
private:
|
||||
bool _request(const QString& url, const QString& sid, const QString& filename, QByteArray* data);
|
||||
|
||||
private slots:
|
||||
void _on_data_ready(); // 有数据可读了,读取并写入文件
|
||||
void _on_finished(); // 下载结束了
|
||||
|
||||
private:
|
||||
QFile m_file;
|
||||
QByteArray* m_data;
|
||||
|
||||
bool m_result;
|
||||
QNetworkReply* m_reply;
|
||||
};
|
||||
|
||||
typedef struct DownloadParam {
|
||||
QString url;
|
||||
QString sid;
|
||||
QString fname;
|
||||
}DownloadParam;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,98 @@
|
|||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QTextCodec>
|
||||
|
||||
// 编译出来的可执行程序复制到单独目录,然后执行 windeployqt 应用程序文件名
|
||||
// 即可自动将依赖的动态库等复制到此目录中。有些文件是多余的,可以酌情删除。
|
||||
|
||||
// 命令行参数格式:
|
||||
// ## 本地文件或目录
|
||||
// tp-player.exe path/of/tp-rdp.tpr 一个 .tpr 文件的文件名
|
||||
// tp-player.exe path/contains/tp-rdp.tpr 包含 .tpr 文件的路径
|
||||
//
|
||||
// ## 从TP服务器上下载
|
||||
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意,并不直接访问此URI,实际上其并不存在)
|
||||
// TP服务器地址(可能包含子路径,例如上例中的{sub/path}部分)/session-id(用于判断当前授权用户)/录像会话编号
|
||||
// 按 “/” 进行分割后,去掉最后两个项,剩下部分是TP服务器的WEB地址,用于合成后续的文件下载URL。
|
||||
// 根据下载的.tpr文件内容,本地合成类似于 "000000256-admin-administrator-123.45.77.88-20191109-020047" 的路径来存放下载的文件
|
||||
// 特别注意,如果账号是 domain\user 这种形式,需要将 "\" 替换为下划线,否则此符号作为路径分隔符,会导致路径不存在而无法保存下载的文件。
|
||||
// - 获取文件大小: http://127.0.0.1:7190/audit/get-file?act=size&type=rdp&rid=yyyyy&f=file-name
|
||||
// - 'act'为`size`表示获取文件大小(返回一个数字字符串,就是指定的文件大小)
|
||||
// - 'type'可以是`rdp`或`ssh`,目前仅用了`rdp`
|
||||
// - 'rid'是录像会话编号(在服务端,一个会话的录像文件均放在录像会话编号命名的目录下)
|
||||
// - 'f' 是文件名,即会话编号目录下的指定文件,例如 'tp-rdp.tpr'
|
||||
// - 读取文件内容: http://127.0.0.1:7190/audit/get-file?act=read&type=rdp&rid=yyyyy&f=file-name&offset=1234&length=1024
|
||||
// - 'act'为`read`表示获取文件内容
|
||||
// - 'offset'表示要读取的偏移,如果未指定,则表示从文件起始处开始读取,即默认为 offset=0
|
||||
// - 'length'表示要读取的大小,如果未指定,表示读取整个文件,即默认为 length=-1(服务端对length=-1做完全读取处理)
|
||||
// - 搭配使用 offst 和 length 可以做到分块下载、断点续传。
|
||||
|
||||
|
||||
void show_usage(QCommandLineParser& parser) {
|
||||
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
|
||||
"<html><head/><body><pre>"
|
||||
+ parser.helpText()
|
||||
+ "\n\n"
|
||||
+ "RESOURCE could be:\n"
|
||||
+ " - teleport record file (.tpr).\n"
|
||||
+ " - a directory contains .tpr file.\n"
|
||||
+ " - an URL to download teleport record file."
|
||||
+ "</pre></body></html>");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
//#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
//#endif
|
||||
|
||||
|
||||
QApplication a(argc, argv);
|
||||
|
||||
//#ifdef __APPLE__
|
||||
// QString data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
// data_path_base += "/tp-testdata/";
|
||||
//#else
|
||||
// QString data_path_base = QCoreApplication::applicationDirPath() + "/record";
|
||||
//#endif
|
||||
// qDebug("data-path-base: %s", data_path_base.toStdString().c_str());
|
||||
// return 0;
|
||||
|
||||
QGuiApplication::setApplicationDisplayName(LOCAL8BIT("[Teleport播放器]"));
|
||||
|
||||
QCommandLineParser parser;
|
||||
const QCommandLineOption opt_help = parser.addHelpOption();
|
||||
|
||||
parser.addPositionalArgument("RESOURCE", "teleport record resource to be play.");
|
||||
|
||||
if(!parser.parse(QCoreApplication::arguments())) {
|
||||
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
|
||||
//"<html><head/><body><h2 style=\"color:#ff0000;\">" + parser.errorText() + "</h2><pre>"
|
||||
"<html><head/><body><h2>" + parser.errorText() + "</h2><pre>"
|
||||
+ parser.helpText() + "</pre></body></html>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(parser.isSet(opt_help)) {
|
||||
show_usage(parser);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if(0 == args.size()) {
|
||||
show_usage(parser);
|
||||
return 2;
|
||||
}
|
||||
|
||||
QString resource = args.at(0);
|
||||
qDebug() << resource;
|
||||
|
||||
|
||||
MainWindow w;
|
||||
w.set_resource(resource);
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
|
@ -0,0 +1,433 @@
|
|||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QMatrix>
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QDesktopWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
static inline int min(int a, int b){
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static inline int max(int a, int b){
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
m_show_default = true;
|
||||
m_bar_shown = false;
|
||||
m_bar_fade_in = false;
|
||||
m_bar_fading = false;
|
||||
m_bar_opacity = 1.0;
|
||||
m_show_message = false;
|
||||
memset(&m_pt, 0, sizeof(TS_RECORD_RDP_POINTER));
|
||||
|
||||
m_thr_play = nullptr;
|
||||
m_play_state = PLAY_STATE_UNKNOWN;
|
||||
m_thr_data = nullptr;
|
||||
|
||||
m_disable_draw = false;
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->centralWidget->setMouseTracking(true);
|
||||
setMouseTracking(true);
|
||||
|
||||
// frame-less window.
|
||||
//#ifdef __APPLE__
|
||||
// setWindowFlags(Qt::FramelessWindowHint | Qt::MSWindowsFixedSizeDialogHint | Qt::Window);
|
||||
// OSXCode::fixWin(winId());
|
||||
//#else
|
||||
// setWindowFlags(Qt::FramelessWindowHint | Qt::MSWindowsFixedSizeDialogHint | windowFlags());
|
||||
//#endif //__APPLE__
|
||||
|
||||
m_pt_normal.load(":/tp-player/res/cursor.png");
|
||||
m_default_bg.load(":/tp-player/res/bg.png");
|
||||
|
||||
m_canvas = QPixmap(m_default_bg.width(), m_default_bg.height());
|
||||
QPainter pp(&m_canvas);
|
||||
pp.drawPixmap(0, 0, m_default_bg, 0, 0, m_default_bg.width(), m_default_bg.height());
|
||||
|
||||
|
||||
setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint); // 禁止最大化按钮
|
||||
setFixedSize(m_default_bg.width(), m_default_bg.height()); // 禁止拖动窗口大小
|
||||
|
||||
if(!m_bar.init(this)) {
|
||||
qDebug("bar init failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
connect(&m_timer_first_run, SIGNAL(timeout()), this, SLOT(_do_first_run()));
|
||||
connect(&m_timer_bar_fade, SIGNAL(timeout()), this, SLOT(_do_bar_fade()));
|
||||
connect(&m_timer_bar_delay_hide, SIGNAL(timeout()), this, SLOT(_do_bar_delay_hide()));
|
||||
|
||||
m_timer_first_run.setSingleShot(true);
|
||||
m_timer_first_run.start(500);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
if(m_thr_play) {
|
||||
m_thr_play->stop();
|
||||
|
||||
disconnect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
|
||||
|
||||
delete m_thr_play;
|
||||
m_thr_play = nullptr;
|
||||
}
|
||||
|
||||
if(m_thr_data) {
|
||||
m_thr_data->stop();
|
||||
disconnect(m_thr_data, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
|
||||
delete m_thr_data;
|
||||
m_thr_data = nullptr;
|
||||
}
|
||||
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::set_resource(const QString &res) {
|
||||
m_res = res;
|
||||
}
|
||||
|
||||
void MainWindow::_do_first_run() {
|
||||
m_thr_data = new ThrData(this, m_res);
|
||||
connect(m_thr_data, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
|
||||
m_thr_data->start(QThread::TimeCriticalPriority);
|
||||
|
||||
m_thr_play = new ThrPlay(this);
|
||||
connect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
|
||||
|
||||
m_thr_play->speed(m_bar.get_speed());
|
||||
m_thr_play->start();
|
||||
}
|
||||
|
||||
void MainWindow::set_speed(int s) {
|
||||
if(m_thr_play)
|
||||
m_thr_play->speed(s);
|
||||
}
|
||||
|
||||
void MainWindow::set_skip(bool s) {
|
||||
if(m_thr_play)
|
||||
m_thr_play->skip(s);
|
||||
}
|
||||
|
||||
void MainWindow::paintEvent(QPaintEvent *e)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
if(m_show_default) {
|
||||
painter.drawPixmap(e->rect(), m_default_bg, e->rect());
|
||||
}
|
||||
else {
|
||||
painter.drawPixmap(e->rect(), m_canvas, e->rect());
|
||||
|
||||
QRect rcpt(m_pt_normal.rect());
|
||||
rcpt.moveTo(m_pt.x - m_pt_normal.width()/2, m_pt.y-m_pt_normal.height()/2);
|
||||
if(e->rect().intersects(rcpt)) {
|
||||
painter.drawPixmap(m_pt.x-m_pt_normal.width()/2, m_pt.y-m_pt_normal.height()/2, m_pt_normal);
|
||||
}
|
||||
|
||||
// 绘制浮动控制窗
|
||||
if(m_bar_fading) {
|
||||
painter.setOpacity(m_bar_opacity);
|
||||
m_bar.draw(painter, e->rect());
|
||||
}
|
||||
else if(m_bar_shown) {
|
||||
m_bar.draw(painter, e->rect());
|
||||
}
|
||||
}
|
||||
|
||||
if(m_show_message) {
|
||||
QRect rc_draw = e->rect();
|
||||
QRect rc(m_rc_message);
|
||||
|
||||
if(e->rect().intersects(rc)) {
|
||||
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
|
||||
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
|
||||
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
|
||||
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
|
||||
int to_x = rc.left() + from_x;
|
||||
int to_y = rc.top() + from_y;
|
||||
painter.drawPixmap(to_x, to_y, m_img_message, from_x, from_y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::pause() {
|
||||
if(m_play_state != PLAY_STATE_RUNNING)
|
||||
return;
|
||||
m_thr_play->pause();
|
||||
m_play_state = PLAY_STATE_PAUSE;
|
||||
}
|
||||
|
||||
void MainWindow::resume(bool relocate, uint32_t ms) {
|
||||
if(m_play_state == PLAY_STATE_PAUSE) {
|
||||
if(relocate)
|
||||
m_thr_data->restart(ms);
|
||||
m_thr_play->resume(relocate, ms);
|
||||
}
|
||||
else if(m_play_state == PLAY_STATE_STOP) {
|
||||
m_thr_data->restart(ms);
|
||||
m_thr_play->resume(true, ms);
|
||||
}
|
||||
|
||||
m_play_state = PLAY_STATE_RUNNING;
|
||||
}
|
||||
|
||||
void MainWindow::_do_update_data(UpdateData* dat) {
|
||||
if(!dat)
|
||||
return;
|
||||
|
||||
UpdateDataHelper data_helper(dat);
|
||||
|
||||
if(dat->data_type() == TYPE_POINTER) {
|
||||
TS_RECORD_RDP_POINTER pt;
|
||||
memcpy(&pt, &m_pt, sizeof(TS_RECORD_RDP_POINTER));
|
||||
|
||||
// 更新虚拟鼠标信息,这样下一次绘制界面时就会在新的位置绘制出虚拟鼠标
|
||||
memcpy(&m_pt, dat->get_pointer(), sizeof(TS_RECORD_RDP_POINTER));
|
||||
update(m_pt.x - m_pt_normal.width()/2, m_pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height());
|
||||
|
||||
update(pt.x - m_pt_normal.width()/2, pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height());
|
||||
|
||||
return;
|
||||
}
|
||||
else if(dat->data_type() == TYPE_IMAGE) {
|
||||
const UpdateImages uimgs = dat->get_images();
|
||||
if(uimgs.size() == 0)
|
||||
return;
|
||||
|
||||
if(uimgs.size() > 1 && !m_disable_draw) {
|
||||
// 禁止界面更新
|
||||
setUpdatesEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
QPainter pp(&m_canvas);
|
||||
for(int i = 0; i < uimgs.size(); ++i) {
|
||||
pp.drawImage(uimgs[i].x, uimgs[i].y, *(uimgs[i].img), 0, 0, uimgs[i].w, uimgs[i].h, Qt::AutoColor);
|
||||
|
||||
if(!m_disable_draw)
|
||||
update(uimgs[i].x, uimgs[i].y, uimgs[i].w, uimgs[i].h);
|
||||
}
|
||||
|
||||
|
||||
if(uimgs.size() > 1 && !m_disable_draw) {
|
||||
// 允许界面更新
|
||||
setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
else if(dat->data_type() == TYPE_PLAYED_MS) {
|
||||
m_bar.update_passed_time(dat->played_ms());
|
||||
return;
|
||||
}
|
||||
|
||||
else if(dat->data_type() == TYPE_DISABLE_DRAW) {
|
||||
// 禁止界面更新
|
||||
m_disable_draw = true;
|
||||
setUpdatesEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
else if(dat->data_type() == TYPE_ENABLE_DRAW) {
|
||||
// 允许界面更新
|
||||
m_disable_draw = false;
|
||||
setUpdatesEnabled(true);
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
else if(dat->data_type() == TYPE_MESSAGE) {
|
||||
if(dat->message().isEmpty()) {
|
||||
m_show_message = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_show_message = true;
|
||||
|
||||
qDebug("1message, w=%d, h=%d", m_canvas.width(), m_canvas.height());
|
||||
|
||||
QPainter pp(&m_canvas);
|
||||
QRect rcWin(0, 0, m_canvas.width(), m_canvas.height());
|
||||
pp.drawText(rcWin, Qt::AlignLeft|Qt::TextDontPrint, dat->message(), &m_rc_message);
|
||||
|
||||
qDebug("2message, w=%d, h=%d", m_rc_message.width(), m_rc_message.height());
|
||||
m_rc_message.setWidth(m_rc_message.width()+60);
|
||||
m_rc_message.setHeight(m_rc_message.height()+60);
|
||||
|
||||
m_img_message = QPixmap(m_rc_message.width(), m_rc_message.height());
|
||||
m_img_message.fill(Qt::transparent);
|
||||
QPainter pm(&m_img_message);
|
||||
pm.setPen(QColor(255,255,255,153));
|
||||
pm.fillRect(m_rc_message, QColor(0,0,0,190));
|
||||
|
||||
QRect rcRect(m_rc_message);
|
||||
rcRect.setWidth(rcRect.width()-1);
|
||||
rcRect.setHeight(rcRect.height()-1);
|
||||
pm.drawRect(rcRect);
|
||||
|
||||
QRect rcText(m_rc_message);
|
||||
rcText.setLeft(30);
|
||||
rcText.setTop(30);
|
||||
pm.drawText(rcText, Qt::AlignLeft, dat->message());
|
||||
m_rc_message.moveTo(
|
||||
(m_canvas.width() - m_rc_message.width())/2,
|
||||
(m_canvas.height() - m_rc_message.height())/3
|
||||
);
|
||||
|
||||
update(m_rc_message.x(), m_rc_message.y(), m_rc_message.width(), m_rc_message.height());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
else if(dat->data_type() == TYPE_ERROR) {
|
||||
QMessageBox::critical(this, QGuiApplication::applicationDisplayName(), dat->message());
|
||||
QApplication::instance()->exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 这是播放开始时收到的第一个数据包
|
||||
else if(dat->data_type() == TYPE_HEADER_INFO) {
|
||||
TS_RECORD_HEADER* hdr = dat->get_header();
|
||||
if(hdr == nullptr)
|
||||
return;
|
||||
memcpy(&m_rec_hdr, hdr, sizeof(TS_RECORD_HEADER));
|
||||
|
||||
qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")";
|
||||
|
||||
m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
|
||||
QDesktopWidget *desktop = QApplication::desktop(); // =qApp->desktop();也可以
|
||||
qDebug("desktop w:%d,h:%d, this w:%d,h:%d", desktop->width(), desktop->height(), width(), height());
|
||||
move(10, (desktop->height() - m_rec_hdr.basic.height)/2);
|
||||
|
||||
setFixedSize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
|
||||
m_canvas.fill(QColor(38, 73, 111));
|
||||
|
||||
m_show_default = false;
|
||||
repaint();
|
||||
|
||||
m_bar.start(m_rec_hdr.info.time_ms, 640);
|
||||
m_bar_shown = true;
|
||||
m_play_state = PLAY_STATE_RUNNING;
|
||||
|
||||
update(m_bar.rc());
|
||||
|
||||
m_bar_fade_in = false;
|
||||
m_bar_fading = true;
|
||||
m_timer_bar_delay_hide.start(2000);
|
||||
|
||||
QString title;
|
||||
if (m_rec_hdr.basic.conn_port == 3389) {
|
||||
title = QString(LOCAL8BIT("用户 %1 访问 %2 的 %3 账号").arg(m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, m_rec_hdr.basic.acc_username));
|
||||
}
|
||||
else {
|
||||
QString _port;
|
||||
_port.sprintf("%d", m_rec_hdr.basic.conn_port);
|
||||
title = QString(LOCAL8BIT("用户 %1 访问 %2:%3 的 %4 账号").arg(m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, _port, m_rec_hdr.basic.acc_username));
|
||||
}
|
||||
|
||||
setWindowTitle(title);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
else if(dat->data_type() == TYPE_END) {
|
||||
m_bar.end();
|
||||
m_play_state = PLAY_STATE_STOP;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::_do_bar_delay_hide() {
|
||||
m_bar_fading = true;
|
||||
m_timer_bar_delay_hide.stop();
|
||||
m_timer_bar_fade.stop();
|
||||
m_timer_bar_fade.start(50);
|
||||
}
|
||||
|
||||
void MainWindow::_do_bar_fade() {
|
||||
if(m_bar_fade_in) {
|
||||
if(m_bar_opacity < 1.0)
|
||||
m_bar_opacity += 0.3;
|
||||
if(m_bar_opacity >= 1.0) {
|
||||
m_bar_opacity = 1.0;
|
||||
m_bar_shown = true;
|
||||
m_bar_fading = false;
|
||||
m_timer_bar_fade.stop();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(m_bar_opacity > 0.0)
|
||||
m_bar_opacity -= 0.2;
|
||||
if(m_bar_opacity <= 0.0) {
|
||||
m_bar_opacity = 0.0;
|
||||
m_bar_shown = false;
|
||||
m_bar_fading = false;
|
||||
m_timer_bar_fade.stop();
|
||||
}
|
||||
}
|
||||
|
||||
update(m_bar.rc());
|
||||
}
|
||||
|
||||
void MainWindow::mouseMoveEvent(QMouseEvent *e) {
|
||||
if(!m_show_default) {
|
||||
QRect rc = m_bar.rc();
|
||||
if(e->y() > rc.top() - 20 && e->y() < rc.bottom() + 20) {
|
||||
if((!m_bar_shown && !m_bar_fading) || (m_bar_fading && !m_bar_fade_in)) {
|
||||
m_bar_fade_in = true;
|
||||
m_bar_fading = true;
|
||||
|
||||
m_timer_bar_delay_hide.stop();
|
||||
m_timer_bar_fade.stop();
|
||||
m_timer_bar_fade.start(50);
|
||||
}
|
||||
|
||||
if(rc.contains(e->pos()))
|
||||
m_bar.onMouseMove(e->x(), e->y());
|
||||
}
|
||||
else {
|
||||
if((m_bar_shown && !m_bar_fading) || (m_bar_fading && m_bar_fade_in)) {
|
||||
m_bar_fade_in = false;
|
||||
m_bar_fading = true;
|
||||
m_timer_bar_fade.stop();
|
||||
m_timer_bar_delay_hide.stop();
|
||||
|
||||
if(m_bar_opacity != 1.0)
|
||||
m_timer_bar_fade.start(50);
|
||||
else
|
||||
m_timer_bar_delay_hide.start(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::mousePressEvent(QMouseEvent *e) {
|
||||
if(!m_show_default) {
|
||||
QRect rc = m_bar.rc();
|
||||
if(rc.contains(e->pos())) {
|
||||
m_bar.onMousePress(e->x(), e->y(), e->button());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::mouseReleaseEvent(QMouseEvent *e) {
|
||||
m_bar.onMouseRelease(e->x(), e->y(), e->button());
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include "bar.h"
|
||||
#include "thr_play.h"
|
||||
#include "thr_data.h"
|
||||
#include "update_data.h"
|
||||
#include "record_format.h"
|
||||
#include "util.h"
|
||||
#include "downloader.h"
|
||||
|
||||
#define PLAY_STATE_UNKNOWN 0
|
||||
#define PLAY_STATE_RUNNING 1
|
||||
#define PLAY_STATE_PAUSE 2
|
||||
#define PLAY_STATE_STOP 3
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
void set_resource(const QString& res);
|
||||
|
||||
void pause();
|
||||
void resume(bool relocate, uint32_t ms);
|
||||
void restart();
|
||||
void set_speed(int s);
|
||||
void set_skip(bool s);
|
||||
|
||||
// TODO: 将thr_data移动到thr_play线程,由play线程进行管理
|
||||
ThrData* get_thr_data() {return m_thr_data;}
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void mouseMoveEvent(QMouseEvent *e);
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
|
||||
private slots:
|
||||
void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载)
|
||||
void _do_update_data(UpdateData*);
|
||||
void _do_bar_fade();
|
||||
void _do_bar_delay_hide();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
|
||||
bool m_show_default;
|
||||
bool m_bar_shown;
|
||||
QPixmap m_default_bg;
|
||||
|
||||
QString m_res;
|
||||
ThrPlay* m_thr_play;
|
||||
ThrData* m_thr_data;
|
||||
|
||||
QPixmap m_canvas;
|
||||
|
||||
Bar m_bar;
|
||||
|
||||
TS_RECORD_HEADER m_rec_hdr;
|
||||
|
||||
QPixmap m_pt_normal;
|
||||
TS_RECORD_RDP_POINTER m_pt;
|
||||
|
||||
QTimer m_timer_first_run;
|
||||
QTimer m_timer_bar_fade;
|
||||
QTimer m_timer_bar_delay_hide;
|
||||
bool m_bar_fade_in;
|
||||
bool m_bar_fading;
|
||||
qreal m_bar_opacity;
|
||||
|
||||
int m_play_state;
|
||||
|
||||
bool m_show_message;
|
||||
QPixmap m_img_message;
|
||||
QRect m_rc_message;
|
||||
bool m_disable_draw;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>360</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>微软雅黑 Light</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Teleport Replayer</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget"/>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,109 @@
|
|||
#ifndef RECORD_FORMAT_H
|
||||
#define RECORD_FORMAT_H
|
||||
|
||||
#include <Qt>
|
||||
|
||||
#define TS_TPPR_TYPE_UNKNOWN 0x0000
|
||||
#define TS_TPPR_TYPE_SSH 0x0001
|
||||
#define TS_TPPR_TYPE_RDP 0x0101
|
||||
|
||||
|
||||
#define TS_RECORD_TYPE_RDP_POINTER 0x12 // 鼠标坐标位置改变,用于绘制虚拟鼠标
|
||||
#define TS_RECORD_TYPE_RDP_IMAGE 0x13 // 服务端返回的图像,用于展示
|
||||
#define TS_RECORD_TYPE_RDP_KEYFRAME 0x14 //
|
||||
|
||||
#define TS_RDP_BTN_FREE 0
|
||||
#define TS_RDP_BTN_PRESSED 1
|
||||
#define TS_RDP_IMG_RAW 0 // 未压缩,原始数据(根据bitsPerPixel,多个字节对应一个点的颜色)
|
||||
#define TS_RDP_IMG_BMP 1 // 压缩的BMP数据
|
||||
#define TS_RDP_IMG_ALT 2
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
// 录像文件头(随着录像数据写入,会改变的部分)
|
||||
typedef struct TS_RECORD_HEADER_INFO {
|
||||
uint32_t magic; // "TPPR" 标志 TelePort Protocol Record
|
||||
uint16_t ver; // 录像文件版本,从3.5.0开始,为4
|
||||
uint16_t type; //
|
||||
// uint32_t packages; // 总包数
|
||||
uint32_t time_ms; // 总耗时(毫秒)
|
||||
uint32_t dat_file_count; // 数据文件数量
|
||||
uint8_t _reserve[64-4-2-2-4-4];
|
||||
}TS_RECORD_HEADER_INFO;
|
||||
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
|
||||
|
||||
// 录像文件头(固定不变部分)
|
||||
typedef struct TS_RECORD_HEADER_BASIC {
|
||||
uint16_t protocol_type; // 协议:1=RDP, 2=SSH, 3=Telnet
|
||||
uint16_t protocol_sub_type; // 子协议:100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
|
||||
uint64_t timestamp; // 本次录像的起始时间(UTC时间戳)
|
||||
uint16_t width; // 初始屏幕尺寸:宽
|
||||
uint16_t height; // 初始屏幕尺寸:高
|
||||
char user_username[64]; // teleport账号
|
||||
char acc_username[64]; // 远程主机用户名
|
||||
|
||||
char host_ip[40]; // 远程主机IP
|
||||
char conn_ip[40]; // 远程主机IP
|
||||
uint16_t conn_port; // 远程主机端口
|
||||
|
||||
char client_ip[40]; // 客户端IP
|
||||
|
||||
// // RDP专有
|
||||
// uint8_t rdp_security; // 0 = RDP, 1 = TLS
|
||||
|
||||
// uint8_t _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - 1 - ts_record_header_info_size];
|
||||
uint8_t _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - ts_record_header_info_size];
|
||||
}TS_RECORD_HEADER_BASIC;
|
||||
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
|
||||
|
||||
typedef struct TS_RECORD_HEADER {
|
||||
TS_RECORD_HEADER_INFO info;
|
||||
TS_RECORD_HEADER_BASIC basic;
|
||||
}TS_RECORD_HEADER;
|
||||
|
||||
// header部分(header-info + header-basic) = 512B
|
||||
#define ts_record_header_size sizeof(TS_RECORD_HEADER)
|
||||
|
||||
// 一个数据包的头
|
||||
typedef struct TS_RECORD_PKG {
|
||||
uint8_t type; // 包的数据类型
|
||||
uint8_t _reserve[3]; // 保留
|
||||
uint32_t size; // 这个包的总大小(不含包头)
|
||||
uint32_t time_ms; // 这个包距起始时间的时间差(毫秒,意味着一个连接不能持续超过49天)
|
||||
// uint32_t index; // 这个包的序号(最后一个包的序号与TS_RECORD_HEADER_INFO::packages数量匹配)
|
||||
}TS_RECORD_PKG;
|
||||
|
||||
|
||||
typedef struct TS_RECORD_RDP_POINTER {
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
uint8_t button;
|
||||
uint8_t pressed;
|
||||
}TS_RECORD_RDP_POINTER;
|
||||
|
||||
// RDP图像更新
|
||||
typedef struct TS_RECORD_RDP_IMAGE_INFO {
|
||||
uint16_t destLeft;
|
||||
uint16_t destTop;
|
||||
uint16_t destRight;
|
||||
uint16_t destBottom;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t bitsPerPixel;
|
||||
uint8_t format;
|
||||
uint8_t _reserved;
|
||||
uint32_t dat_len;
|
||||
uint32_t zip_len;
|
||||
}TS_RECORD_RDP_IMAGE_INFO;
|
||||
|
||||
// 关键帧索引
|
||||
typedef struct TS_RECORD_RDP_KEYFRAME_INFO {
|
||||
uint32_t time_ms; // 此关键帧的时间点
|
||||
uint32_t file_index; // 此关键帧图像数据位于哪一个数据文件中
|
||||
uint32_t offset; // 此关键帧图像数据在数据文件中的偏移
|
||||
}TS_RECORD_RDP_KEYFRAME_INFO;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
#endif // RECORD_FORMAT_H
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1016 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1016 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1008 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1016 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 1003 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1004 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,974 @@
|
|||
/* -*- c-basic-offset: 8 -*-
|
||||
rdesktop: A Remote Desktop Protocol client.
|
||||
Bitmap decompression routines
|
||||
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* three seperate function for speed when decompressing the bitmaps
|
||||
when modifing one function make the change in the others
|
||||
jay.sorg@gmail.com */
|
||||
|
||||
/* indent is confused by this file */
|
||||
/* *INDENT-OFF* */
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "rle.h"
|
||||
|
||||
/* Specific rename for RDPY integration */
|
||||
#define unimpl(str, code)
|
||||
|
||||
//#define RD_BOOL int
|
||||
//#define False 0
|
||||
//#define True 1
|
||||
/* end specific rename */
|
||||
|
||||
#define CVAL(p) (*(p++))
|
||||
//#ifdef NEED_ALIGN
|
||||
//#ifdef L_ENDIAN
|
||||
#define CVAL2(p, v) { v = (*(p++)); v |= (*(p++)) << 8; }
|
||||
//#else
|
||||
//#define CVAL2(p, v) { v = (*(p++)) << 8; v |= (*(p++)); }
|
||||
//#endif /* L_ENDIAN */
|
||||
//#else
|
||||
//#define CVAL2(p, v) { v = (*((uint16*)p)); p += 2; }
|
||||
//#endif /* NEED_ALIGN */
|
||||
|
||||
#define UNROLL8(exp) { exp exp exp exp exp exp exp exp }
|
||||
|
||||
#define REPEAT(statement) \
|
||||
{ \
|
||||
while((count & ~0x7) && ((x+8) < width)) \
|
||||
UNROLL8( statement; count--; x++; ); \
|
||||
\
|
||||
while((count > 0) && (x < width)) \
|
||||
{ \
|
||||
statement; \
|
||||
count--; \
|
||||
x++; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define MASK_UPDATE() \
|
||||
{ \
|
||||
mixmask <<= 1; \
|
||||
if (mixmask == 0) \
|
||||
{ \
|
||||
mask = fom_mask ? fom_mask : CVAL(input); \
|
||||
mixmask = 1; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* 1 byte bitmap decompress */
|
||||
RD_BOOL
|
||||
bitmap_decompress1(uint8 * output, int width, int height, const uint8 * input, int size)
|
||||
{
|
||||
const uint8 *end = input + size;
|
||||
uint8 *prevline = NULL, *line = NULL;
|
||||
int opcode, count, offset, isfillormix, x = width;
|
||||
int lastopcode = -1, insertmix = False, bicolour = False;
|
||||
uint8 code;
|
||||
uint8 colour1 = 0, colour2 = 0;
|
||||
uint8 mixmask, mask = 0;
|
||||
uint8 mix = 0xff;
|
||||
int fom_mask = 0;
|
||||
|
||||
while (input < end)
|
||||
{
|
||||
fom_mask = 0;
|
||||
code = CVAL(input);
|
||||
opcode = code >> 4;
|
||||
/* Handle different opcode forms */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
opcode -= 6;
|
||||
count = code & 0xf;
|
||||
offset = 16;
|
||||
break;
|
||||
case 0xf:
|
||||
opcode = code & 0xf;
|
||||
if (opcode < 9)
|
||||
{
|
||||
count = CVAL(input);
|
||||
count |= CVAL(input) << 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (opcode < 0xb) ? 8 : 1;
|
||||
}
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
opcode >>= 1;
|
||||
count = code & 0x1f;
|
||||
offset = 32;
|
||||
break;
|
||||
}
|
||||
/* Handle strange cases for counts */
|
||||
if (offset != 0)
|
||||
{
|
||||
isfillormix = ((opcode == 2) || (opcode == 7));
|
||||
if (count == 0)
|
||||
{
|
||||
if (isfillormix)
|
||||
count = CVAL(input) + 1;
|
||||
else
|
||||
count = CVAL(input) + offset;
|
||||
}
|
||||
else if (isfillormix)
|
||||
{
|
||||
count <<= 3;
|
||||
}
|
||||
}
|
||||
/* Read preliminary data */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
|
||||
insertmix = True;
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
colour1 = CVAL(input);
|
||||
case 3: /* Colour */
|
||||
colour2 = CVAL(input);
|
||||
break;
|
||||
case 6: /* SetMix/Mix */
|
||||
case 7: /* SetMix/FillOrMix */
|
||||
mix = CVAL(input);
|
||||
opcode -= 5;
|
||||
break;
|
||||
case 9: /* FillOrMix_1 */
|
||||
mask = 0x03;
|
||||
opcode = 0x02;
|
||||
fom_mask = 3;
|
||||
break;
|
||||
case 0x0a: /* FillOrMix_2 */
|
||||
mask = 0x05;
|
||||
opcode = 0x02;
|
||||
fom_mask = 5;
|
||||
break;
|
||||
}
|
||||
lastopcode = opcode;
|
||||
mixmask = 0;
|
||||
/* Output body */
|
||||
while (count > 0)
|
||||
{
|
||||
if (x >= width)
|
||||
{
|
||||
if (height <= 0)
|
||||
return False;
|
||||
x = 0;
|
||||
height--;
|
||||
prevline = line;
|
||||
line = output + height * width;
|
||||
}
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if (insertmix)
|
||||
{
|
||||
if (prevline == NULL)
|
||||
line[x] = mix;
|
||||
else
|
||||
line[x] = prevline[x] ^ mix;
|
||||
insertmix = False;
|
||||
count--;
|
||||
x++;
|
||||
}
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT(line[x] = 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT(line[x] = prevline[x])
|
||||
}
|
||||
break;
|
||||
case 1: /* Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT(line[x] = mix)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT(line[x] = prevline[x] ^ mix)
|
||||
}
|
||||
break;
|
||||
case 2: /* Fill or Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
line[x] = mix;
|
||||
else
|
||||
line[x] = 0;
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
line[x] = prevline[x] ^ mix;
|
||||
else
|
||||
line[x] = prevline[x];
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 3: /* Colour */
|
||||
REPEAT(line[x] = colour2)
|
||||
break;
|
||||
case 4: /* Copy */
|
||||
REPEAT(line[x] = CVAL(input))
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
REPEAT
|
||||
(
|
||||
if (bicolour)
|
||||
{
|
||||
line[x] = colour2;
|
||||
bicolour = False;
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x] = colour1;
|
||||
bicolour = True; count++;
|
||||
}
|
||||
)
|
||||
break;
|
||||
case 0xd: /* White */
|
||||
REPEAT(line[x] = 0xff)
|
||||
break;
|
||||
case 0xe: /* Black */
|
||||
REPEAT(line[x] = 0)
|
||||
break;
|
||||
default:
|
||||
unimpl("bitmap opcode 0x%x\n", opcode);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/* 2 byte bitmap decompress */
|
||||
RD_BOOL
|
||||
bitmap_decompress2(uint8 * output, int width, int height, const uint8 * input, int size)
|
||||
{
|
||||
const uint8 *end = input + size;
|
||||
uint16 *prevline = NULL, *line = NULL;
|
||||
int opcode, count, offset, isfillormix, x = width;
|
||||
int lastopcode = -1, insertmix = False, bicolour = False;
|
||||
uint8 code;
|
||||
uint16 colour1 = 0, colour2 = 0;
|
||||
uint8 mixmask, mask = 0;
|
||||
uint16 mix = 0xffff;
|
||||
int fom_mask = 0;
|
||||
|
||||
while (input < end)
|
||||
{
|
||||
fom_mask = 0;
|
||||
code = CVAL(input);
|
||||
opcode = code >> 4;
|
||||
/* Handle different opcode forms */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
opcode -= 6;
|
||||
count = code & 0xf;
|
||||
offset = 16;
|
||||
break;
|
||||
case 0xf:
|
||||
opcode = code & 0xf;
|
||||
if (opcode < 9)
|
||||
{
|
||||
count = CVAL(input);
|
||||
count |= CVAL(input) << 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (opcode < 0xb) ? 8 : 1;
|
||||
}
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
opcode >>= 1;
|
||||
count = code & 0x1f;
|
||||
offset = 32;
|
||||
break;
|
||||
}
|
||||
/* Handle strange cases for counts */
|
||||
if (offset != 0)
|
||||
{
|
||||
isfillormix = ((opcode == 2) || (opcode == 7));
|
||||
if (count == 0)
|
||||
{
|
||||
if (isfillormix)
|
||||
count = CVAL(input) + 1;
|
||||
else
|
||||
count = CVAL(input) + offset;
|
||||
}
|
||||
else if (isfillormix)
|
||||
{
|
||||
count <<= 3;
|
||||
}
|
||||
}
|
||||
/* Read preliminary data */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
|
||||
insertmix = True;
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
CVAL2(input, colour1);
|
||||
case 3: /* Colour */
|
||||
CVAL2(input, colour2);
|
||||
break;
|
||||
case 6: /* SetMix/Mix */
|
||||
case 7: /* SetMix/FillOrMix */
|
||||
CVAL2(input, mix);
|
||||
opcode -= 5;
|
||||
break;
|
||||
case 9: /* FillOrMix_1 */
|
||||
mask = 0x03;
|
||||
opcode = 0x02;
|
||||
fom_mask = 3;
|
||||
break;
|
||||
case 0x0a: /* FillOrMix_2 */
|
||||
mask = 0x05;
|
||||
opcode = 0x02;
|
||||
fom_mask = 5;
|
||||
break;
|
||||
}
|
||||
lastopcode = opcode;
|
||||
mixmask = 0;
|
||||
/* Output body */
|
||||
while (count > 0)
|
||||
{
|
||||
if (x >= width)
|
||||
{
|
||||
if (height <= 0)
|
||||
return False;
|
||||
x = 0;
|
||||
height--;
|
||||
prevline = line;
|
||||
line = ((uint16 *) output) + height * width;
|
||||
}
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if (insertmix)
|
||||
{
|
||||
if (prevline == NULL)
|
||||
line[x] = mix;
|
||||
else
|
||||
line[x] = prevline[x] ^ mix;
|
||||
insertmix = False;
|
||||
count--;
|
||||
x++;
|
||||
}
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT(line[x] = 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT(line[x] = prevline[x])
|
||||
}
|
||||
break;
|
||||
case 1: /* Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT(line[x] = mix)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT(line[x] = prevline[x] ^ mix)
|
||||
}
|
||||
break;
|
||||
case 2: /* Fill or Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
line[x] = mix;
|
||||
else
|
||||
line[x] = 0;
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
line[x] = prevline[x] ^ mix;
|
||||
else
|
||||
line[x] = prevline[x];
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 3: /* Colour */
|
||||
REPEAT(line[x] = colour2)
|
||||
break;
|
||||
case 4: /* Copy */
|
||||
REPEAT(CVAL2(input, line[x]))
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
REPEAT
|
||||
(
|
||||
if (bicolour)
|
||||
{
|
||||
line[x] = colour2;
|
||||
bicolour = False;
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x] = colour1;
|
||||
bicolour = True;
|
||||
count++;
|
||||
}
|
||||
)
|
||||
break;
|
||||
case 0xd: /* White */
|
||||
REPEAT(line[x] = 0xffff)
|
||||
break;
|
||||
case 0xe: /* Black */
|
||||
REPEAT(line[x] = 0)
|
||||
break;
|
||||
default:
|
||||
unimpl("bitmap opcode 0x%x\n", opcode);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/* 3 byte bitmap decompress */
|
||||
RD_BOOL
|
||||
bitmap_decompress3(uint8 * output, int width, int height, const uint8 * input, int size)
|
||||
{
|
||||
uint8 *end = input + size;
|
||||
uint8 *prevline = NULL, *line = NULL;
|
||||
int opcode, count, offset, isfillormix, x = width;
|
||||
int lastopcode = -1, insertmix = False, bicolour = False;
|
||||
uint8 code;
|
||||
uint8 colour1[3] = {0, 0, 0}, colour2[3] = {0, 0, 0};
|
||||
uint8 mixmask, mask = 0;
|
||||
uint8 mix[3] = {0xff, 0xff, 0xff};
|
||||
int fom_mask = 0;
|
||||
|
||||
while (input < end)
|
||||
{
|
||||
fom_mask = 0;
|
||||
code = CVAL(input);
|
||||
opcode = code >> 4;
|
||||
/* Handle different opcode forms */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xe:
|
||||
opcode -= 6;
|
||||
count = code & 0xf;
|
||||
offset = 16;
|
||||
break;
|
||||
case 0xf:
|
||||
opcode = code & 0xf;
|
||||
if (opcode < 9)
|
||||
{
|
||||
count = CVAL(input);
|
||||
count |= CVAL(input) << 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (opcode <
|
||||
0xb) ? 8 : 1;
|
||||
}
|
||||
offset = 0;
|
||||
break;
|
||||
default:
|
||||
opcode >>= 1;
|
||||
count = code & 0x1f;
|
||||
offset = 32;
|
||||
break;
|
||||
}
|
||||
/* Handle strange cases for counts */
|
||||
if (offset != 0)
|
||||
{
|
||||
isfillormix = ((opcode == 2) || (opcode == 7));
|
||||
if (count == 0)
|
||||
{
|
||||
if (isfillormix)
|
||||
count = CVAL(input) + 1;
|
||||
else
|
||||
count = CVAL(input) + offset;
|
||||
}
|
||||
else if (isfillormix)
|
||||
{
|
||||
count <<= 3;
|
||||
}
|
||||
}
|
||||
/* Read preliminary data */
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
|
||||
insertmix = True;
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
colour1[0] = CVAL(input);
|
||||
colour1[1] = CVAL(input);
|
||||
colour1[2] = CVAL(input);
|
||||
case 3: /* Colour */
|
||||
colour2[0] = CVAL(input);
|
||||
colour2[1] = CVAL(input);
|
||||
colour2[2] = CVAL(input);
|
||||
break;
|
||||
case 6: /* SetMix/Mix */
|
||||
case 7: /* SetMix/FillOrMix */
|
||||
mix[0] = CVAL(input);
|
||||
mix[1] = CVAL(input);
|
||||
mix[2] = CVAL(input);
|
||||
opcode -= 5;
|
||||
break;
|
||||
case 9: /* FillOrMix_1 */
|
||||
mask = 0x03;
|
||||
opcode = 0x02;
|
||||
fom_mask = 3;
|
||||
break;
|
||||
case 0x0a: /* FillOrMix_2 */
|
||||
mask = 0x05;
|
||||
opcode = 0x02;
|
||||
fom_mask = 5;
|
||||
break;
|
||||
}
|
||||
lastopcode = opcode;
|
||||
mixmask = 0;
|
||||
/* Output body */
|
||||
while (count > 0)
|
||||
{
|
||||
if (x >= width)
|
||||
{
|
||||
if (height <= 0)
|
||||
return False;
|
||||
x = 0;
|
||||
height--;
|
||||
prevline = line;
|
||||
line = output + height * (width * 3);
|
||||
}
|
||||
switch (opcode)
|
||||
{
|
||||
case 0: /* Fill */
|
||||
if (insertmix)
|
||||
{
|
||||
if (prevline == NULL)
|
||||
{
|
||||
line[x * 3] = mix[0];
|
||||
line[x * 3 + 1] = mix[1];
|
||||
line[x * 3 + 2] = mix[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x * 3] =
|
||||
prevline[x * 3] ^ mix[0];
|
||||
line[x * 3 + 1] =
|
||||
prevline[x * 3 + 1] ^ mix[1];
|
||||
line[x * 3 + 2] =
|
||||
prevline[x * 3 + 2] ^ mix[2];
|
||||
}
|
||||
insertmix = False;
|
||||
count--;
|
||||
x++;
|
||||
}
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = 0;
|
||||
line[x * 3 + 1] = 0;
|
||||
line[x * 3 + 2] = 0;
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = prevline[x * 3];
|
||||
line[x * 3 + 1] = prevline[x * 3 + 1];
|
||||
line[x * 3 + 2] = prevline[x * 3 + 2];
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 1: /* Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = mix[0];
|
||||
line[x * 3 + 1] = mix[1];
|
||||
line[x * 3 + 2] = mix[2];
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] =
|
||||
prevline[x * 3] ^ mix[0];
|
||||
line[x * 3 + 1] =
|
||||
prevline[x * 3 + 1] ^ mix[1];
|
||||
line[x * 3 + 2] =
|
||||
prevline[x * 3 + 2] ^ mix[2];
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 2: /* Fill or Mix */
|
||||
if (prevline == NULL)
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
{
|
||||
line[x * 3] = mix[0];
|
||||
line[x * 3 + 1] = mix[1];
|
||||
line[x * 3 + 2] = mix[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x * 3] = 0;
|
||||
line[x * 3 + 1] = 0;
|
||||
line[x * 3 + 2] = 0;
|
||||
}
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
REPEAT
|
||||
(
|
||||
MASK_UPDATE();
|
||||
if (mask & mixmask)
|
||||
{
|
||||
line[x * 3] =
|
||||
prevline[x * 3] ^ mix [0];
|
||||
line[x * 3 + 1] =
|
||||
prevline[x * 3 + 1] ^ mix [1];
|
||||
line[x * 3 + 2] =
|
||||
prevline[x * 3 + 2] ^ mix [2];
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x * 3] =
|
||||
prevline[x * 3];
|
||||
line[x * 3 + 1] =
|
||||
prevline[x * 3 + 1];
|
||||
line[x * 3 + 2] =
|
||||
prevline[x * 3 + 2];
|
||||
}
|
||||
)
|
||||
}
|
||||
break;
|
||||
case 3: /* Colour */
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = colour2 [0];
|
||||
line[x * 3 + 1] = colour2 [1];
|
||||
line[x * 3 + 2] = colour2 [2];
|
||||
)
|
||||
break;
|
||||
case 4: /* Copy */
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = CVAL(input);
|
||||
line[x * 3 + 1] = CVAL(input);
|
||||
line[x * 3 + 2] = CVAL(input);
|
||||
)
|
||||
break;
|
||||
case 8: /* Bicolour */
|
||||
REPEAT
|
||||
(
|
||||
if (bicolour)
|
||||
{
|
||||
line[x * 3] = colour2[0];
|
||||
line[x * 3 + 1] = colour2[1];
|
||||
line[x * 3 + 2] = colour2[2];
|
||||
bicolour = False;
|
||||
}
|
||||
else
|
||||
{
|
||||
line[x * 3] = colour1[0];
|
||||
line[x * 3 + 1] = colour1[1];
|
||||
line[x * 3 + 2] = colour1[2];
|
||||
bicolour = True;
|
||||
count++;
|
||||
}
|
||||
)
|
||||
break;
|
||||
case 0xd: /* White */
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = 0xff;
|
||||
line[x * 3 + 1] = 0xff;
|
||||
line[x * 3 + 2] = 0xff;
|
||||
)
|
||||
break;
|
||||
case 0xe: /* Black */
|
||||
REPEAT
|
||||
(
|
||||
line[x * 3] = 0;
|
||||
line[x * 3 + 1] = 0;
|
||||
line[x * 3 + 2] = 0;
|
||||
)
|
||||
break;
|
||||
default:
|
||||
unimpl("bitmap opcode 0x%x\n", opcode);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/* decompress a colour plane */
|
||||
static int
|
||||
process_plane(uint8 * in, int width, int height, uint8 * out, int size)
|
||||
{
|
||||
int indexw;
|
||||
int indexh;
|
||||
int code;
|
||||
int collen;
|
||||
int replen;
|
||||
int color;
|
||||
int x;
|
||||
int revcode;
|
||||
uint8 * last_line;
|
||||
uint8 * this_line;
|
||||
uint8 * org_in;
|
||||
uint8 * org_out;
|
||||
|
||||
org_in = in;
|
||||
org_out = out;
|
||||
last_line = 0;
|
||||
indexh = 0;
|
||||
while (indexh < height)
|
||||
{
|
||||
out = (org_out + width * height * 4) - ((indexh + 1) * width * 4);
|
||||
color = 0;
|
||||
this_line = out;
|
||||
indexw = 0;
|
||||
if (last_line == 0)
|
||||
{
|
||||
while (indexw < width)
|
||||
{
|
||||
code = CVAL(in);
|
||||
replen = code & 0xf;
|
||||
collen = (code >> 4) & 0xf;
|
||||
revcode = (replen << 4) | collen;
|
||||
if ((revcode <= 47) && (revcode >= 16))
|
||||
{
|
||||
replen = revcode;
|
||||
collen = 0;
|
||||
}
|
||||
while (collen > 0)
|
||||
{
|
||||
color = CVAL(in);
|
||||
*out = color;
|
||||
out += 4;
|
||||
indexw++;
|
||||
collen--;
|
||||
}
|
||||
while (replen > 0)
|
||||
{
|
||||
*out = color;
|
||||
out += 4;
|
||||
indexw++;
|
||||
replen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (indexw < width)
|
||||
{
|
||||
code = CVAL(in);
|
||||
replen = code & 0xf;
|
||||
collen = (code >> 4) & 0xf;
|
||||
revcode = (replen << 4) | collen;
|
||||
if ((revcode <= 47) && (revcode >= 16))
|
||||
{
|
||||
replen = revcode;
|
||||
collen = 0;
|
||||
}
|
||||
while (collen > 0)
|
||||
{
|
||||
x = CVAL(in);
|
||||
if (x & 1)
|
||||
{
|
||||
x = x >> 1;
|
||||
x = x + 1;
|
||||
color = -x;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = x >> 1;
|
||||
color = x;
|
||||
}
|
||||
x = last_line[indexw * 4] + color;
|
||||
*out = x;
|
||||
out += 4;
|
||||
indexw++;
|
||||
collen--;
|
||||
}
|
||||
while (replen > 0)
|
||||
{
|
||||
x = last_line[indexw * 4] + color;
|
||||
*out = x;
|
||||
out += 4;
|
||||
indexw++;
|
||||
replen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
indexh++;
|
||||
last_line = this_line;
|
||||
}
|
||||
return (int) (in - org_in);
|
||||
}
|
||||
|
||||
/* 4 byte bitmap decompress */
|
||||
RD_BOOL
|
||||
bitmap_decompress4(uint8 * output, int width, int height, const uint8 * input, int size)
|
||||
{
|
||||
int code;
|
||||
int bytes_pro;
|
||||
int total_pro;
|
||||
|
||||
code = CVAL(input);
|
||||
if (code != 0x10)
|
||||
{
|
||||
return False;
|
||||
}
|
||||
total_pro = 1;
|
||||
bytes_pro = process_plane(input, width, height, output + 3, size - total_pro);
|
||||
total_pro += bytes_pro;
|
||||
input += bytes_pro;
|
||||
bytes_pro = process_plane(input, width, height, output + 2, size - total_pro);
|
||||
total_pro += bytes_pro;
|
||||
input += bytes_pro;
|
||||
bytes_pro = process_plane(input, width, height, output + 1, size - total_pro);
|
||||
total_pro += bytes_pro;
|
||||
input += bytes_pro;
|
||||
bytes_pro = process_plane(input, width, height, output + 0, size - total_pro);
|
||||
total_pro += bytes_pro;
|
||||
return size == total_pro;
|
||||
}
|
||||
|
||||
int
|
||||
bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
|
||||
uint8 * temp = (uint8*)malloc(input_width * input_height * 2);
|
||||
RD_BOOL rv = bitmap_decompress2(temp, input_width, input_height, input, size);
|
||||
|
||||
// convert to rgba
|
||||
for (int y = 0; y < output_height; y++) {
|
||||
for (int x = 0; x < output_width; x++) {
|
||||
uint16 a = ((uint16*)temp)[y * input_width + x];
|
||||
uint8 r = (a & 0x7c00) >> 10;
|
||||
uint8 g = (a & 0x03e0) >> 5;
|
||||
uint8 b = (a & 0x001f);
|
||||
r = r * 255 / 31;
|
||||
g = g * 255 / 31;
|
||||
b = b * 255 / 31;
|
||||
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
|
||||
}
|
||||
}
|
||||
|
||||
free(temp);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
|
||||
uint8 * temp = (uint8*)malloc(input_width * input_height * 2);
|
||||
RD_BOOL rv = bitmap_decompress2(temp, input_width, input_height, input, size);
|
||||
|
||||
// convert to rgba
|
||||
for (int y = 0; y < output_height; y++) {
|
||||
for (int x = 0; x < output_width; x++) {
|
||||
uint16 a = ((uint16*)temp)[y * input_width + x];
|
||||
uint8 r = (a & 0xf800) >> 11;
|
||||
uint8 g = (a & 0x07e0) >> 5;
|
||||
uint8 b = (a & 0x001f);
|
||||
r = r * 255 / 31;
|
||||
g = g * 255 / 63;
|
||||
b = b * 255 / 31;
|
||||
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
|
||||
}
|
||||
}
|
||||
free(temp);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
bitmap_decompress_24(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
|
||||
uint8 * temp = (uint8*)malloc(input_width * input_height * 3);
|
||||
RD_BOOL rv = bitmap_decompress3(temp, input_width, input_height, input, size);
|
||||
|
||||
// convert to rgba
|
||||
for (int y = 0; y < output_height; y++) {
|
||||
for (int x = 0; x < output_width; x++) {
|
||||
uint8 r = temp[(y * input_width + x) * 3];
|
||||
uint8 g = temp[(y * input_width + x) * 3 + 1];
|
||||
uint8 b = temp[(y * input_width + x) * 3 + 2];
|
||||
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
|
||||
}
|
||||
}
|
||||
free(temp);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
bitmap_decompress_32(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
|
||||
uint8 * temp = (uint8*)malloc(input_width * input_height * 4);
|
||||
RD_BOOL rv = bitmap_decompress4(temp, input_width, input_height, input, size);
|
||||
|
||||
// convert to rgba
|
||||
for (int y = 0; y < output_height; y++) {
|
||||
for (int x = 0; x < output_width; x++) {
|
||||
uint8 r = temp[(y * input_width + x) * 4];
|
||||
uint8 g = temp[(y * input_width + x) * 4 + 1];
|
||||
uint8 b = temp[(y * input_width + x) * 4 + 2];
|
||||
uint8 a = temp[(y * input_width + x) * 4 + 3];
|
||||
((uint32*)output)[y * output_width + x] = 0xff << 24 | r << 16 | g << 8 | b;
|
||||
}
|
||||
}
|
||||
free(temp);
|
||||
|
||||
return rv;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef RLE_H
|
||||
#define RLE_H
|
||||
|
||||
#define RD_BOOL int
|
||||
#define False 0
|
||||
#define True 1
|
||||
|
||||
#define uint8 unsigned char
|
||||
#define uint16 unsigned short
|
||||
#define uint32 unsigned int
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
RD_BOOL bitmap_decompress1(uint8 * output, int width, int height, const uint8 * input, int size);
|
||||
RD_BOOL bitmap_decompress2(uint8 * output, int width, int height, const uint8 * input, int size);
|
||||
RD_BOOL bitmap_decompress3(uint8 * output, int width, int height, const uint8 * input, int size);
|
||||
RD_BOOL bitmap_decompress4(uint8 * output, int width, int height, const uint8 * input, int size);
|
||||
|
||||
int bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
|
||||
int bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
|
||||
int bitmap_decompress_24(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
|
||||
int bitmap_decompress_32(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // RLE_H
|
|
@ -0,0 +1,709 @@
|
|||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QNetworkCookie>
|
||||
#include <QStandardPaths>
|
||||
#include <qcoreapplication.h>
|
||||
#include <inttypes.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "thr_play.h"
|
||||
#include "thr_data.h"
|
||||
#include "util.h"
|
||||
#include "downloader.h"
|
||||
#include "record_format.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "rle.h"
|
||||
|
||||
|
||||
static QImage* _rdpimg2QImage(int w, int h, int bitsPerPixel, bool isCompressed, const uint8_t* dat, uint32_t len) {
|
||||
QImage* out;
|
||||
switch(bitsPerPixel) {
|
||||
case 15:
|
||||
if(isCompressed) {
|
||||
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
|
||||
if(!bitmap_decompress1(_dat, w, h, dat, len)) {
|
||||
free(_dat);
|
||||
return nullptr;
|
||||
}
|
||||
out = new QImage(_dat, w, h, QImage::Format_RGB555);
|
||||
free(_dat);
|
||||
}
|
||||
else {
|
||||
out = new QImage(QImage(dat, w, h, QImage::Format_RGB555).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
|
||||
}
|
||||
return out;
|
||||
|
||||
case 16:
|
||||
if(isCompressed) {
|
||||
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
|
||||
if(!bitmap_decompress2(_dat, w, h, dat, len)) {
|
||||
free(_dat);
|
||||
qDebug() << "22------------------DECOMPRESS2 failed.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: 这里需要进一步优化,直接操作QImage的buffer。
|
||||
out = new QImage(w, h, QImage::Format_RGB16);
|
||||
for(int y = 0; y < h; y++) {
|
||||
for(int x = 0; x < w; x++) {
|
||||
uint16 a = ((uint16*)_dat)[y * w + x];
|
||||
uint8 r = ((a & 0xf800) >> 11) * 255 / 31;
|
||||
uint8 g = ((a & 0x07e0) >> 5) * 255 / 63;
|
||||
uint8 b = (a & 0x001f) * 255 / 31;
|
||||
out->setPixelColor(x, y, QColor(r,g,b));
|
||||
}
|
||||
}
|
||||
free(_dat);
|
||||
return out;
|
||||
}
|
||||
else {
|
||||
out = new QImage(QImage(dat, w, h, QImage::Format_RGB16).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
|
||||
}
|
||||
return out;
|
||||
|
||||
case 24:
|
||||
case 32:
|
||||
default:
|
||||
qDebug() << "--------NOT support UNKNOWN bitsPerPix" << bitsPerPixel;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static QImage* _raw2QImage(int w, int h, const uint8_t* dat, uint32_t len) {
|
||||
QImage* out;
|
||||
|
||||
// TODO: 这里需要进一步优化,直接操作QImage的buffer。
|
||||
out = new QImage(w, h, QImage::Format_RGB16);
|
||||
for(int y = 0; y < h; y++) {
|
||||
for(int x = 0; x < w; x++) {
|
||||
uint16 a = ((uint16*)dat)[y * w + x];
|
||||
uint8 r = ((a & 0xf800) >> 11) * 255 / 31;
|
||||
uint8 g = ((a & 0x07e0) >> 5) * 255 / 63;
|
||||
uint8 b = (a & 0x001f) * 255 / 31;
|
||||
out->setPixelColor(x, y, QColor(r,g,b));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================================================================
|
||||
// ThrData
|
||||
//=================================================================
|
||||
|
||||
ThrData::ThrData(MainWindow* mainwin, const QString& res) {
|
||||
m_mainwin = mainwin;
|
||||
m_res = res;
|
||||
m_need_download = false;
|
||||
m_need_stop = false;
|
||||
m_need_restart = false;
|
||||
m_wait_restart = false;
|
||||
m_need_show_kf = false;
|
||||
|
||||
m_file_idx = 0;
|
||||
m_offset = 0;
|
||||
|
||||
#ifdef __APPLE__
|
||||
m_data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
m_data_path_base += "/tp-testdata/";
|
||||
#else
|
||||
m_data_path_base = QCoreApplication::applicationDirPath() + "/record";
|
||||
#endif
|
||||
qDebug("data-path-base: %s", m_data_path_base.toStdString().c_str());
|
||||
|
||||
// qDebug() << "AppConfigLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
// qDebug() << "AppDataLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
// qDebug() << "AppLocalDataLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
|
||||
// qDebug() << "ConfigLocation:" << QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
|
||||
// qDebug() << "CacheLocation:" << QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
// qDebug() << "GenericCacheLocation:" << QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
|
||||
|
||||
/*
|
||||
AppConfigLocation: "C:/Users/apex/AppData/Local/tp-player"
|
||||
AppDataLocation: "C:/Users/apex/AppData/Roaming/tp-player"
|
||||
AppLocalDataLocation: "C:/Users/apex/AppData/Local/tp-player"
|
||||
ConfigLocation: "C:/Users/apex/AppData/Local/tp-player"
|
||||
CacheLocation: "C:/Users/apex/AppData/Local/tp-player/cache"
|
||||
GenericCacheLocation: "C:/Users/apex/AppData/Local/cache"
|
||||
*/
|
||||
}
|
||||
|
||||
ThrData::~ThrData() {
|
||||
_clear_data();
|
||||
}
|
||||
|
||||
void ThrData::stop() {
|
||||
if(!isRunning())
|
||||
return;
|
||||
m_need_stop = true;
|
||||
wait();
|
||||
qDebug("data thread stop() end.");
|
||||
}
|
||||
|
||||
void ThrData::_notify_message(const QString& msg) {
|
||||
UpdateData* _msg = new UpdateData(TYPE_MESSAGE);
|
||||
_msg->message(msg);
|
||||
emit signal_update_data(_msg);
|
||||
}
|
||||
|
||||
void ThrData::_notify_error(const QString& msg) {
|
||||
UpdateData* _msg = new UpdateData(TYPE_ERROR);
|
||||
_msg->message(msg);
|
||||
emit signal_update_data(_msg);
|
||||
}
|
||||
|
||||
void ThrData::run() {
|
||||
_run();
|
||||
qDebug("ThrData thread run() end.");
|
||||
}
|
||||
|
||||
void ThrData::_run() {
|
||||
|
||||
QString _tmp_res = m_res.toLower();
|
||||
if(_tmp_res.startsWith("http")) {
|
||||
m_need_download = true;
|
||||
_notify_message(LOCAL8BIT("正在准备录像数据,请稍候..."));
|
||||
|
||||
if(!m_thr_download.init(m_data_path_base, m_res)) {
|
||||
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法下载录像文件!\n\n"), m_res));
|
||||
return;
|
||||
}
|
||||
|
||||
m_thr_download.start();
|
||||
msleep(100);
|
||||
|
||||
for(;;) {
|
||||
if(m_need_stop)
|
||||
return;
|
||||
if(!m_thr_download.is_running() || m_thr_download.is_tpk_downloaded())
|
||||
break;
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
if(!m_thr_download.is_tpk_downloaded()) {
|
||||
_notify_error(QString("%1\n%2").arg(LOCAL8BIT("无法下载录像文件!"), m_res));
|
||||
return;
|
||||
}
|
||||
|
||||
m_thr_download.get_data_path(m_data_path);
|
||||
}
|
||||
else {
|
||||
QFileInfo fi_chk_link(m_res);
|
||||
if(fi_chk_link.isSymLink())
|
||||
m_res = fi_chk_link.symLinkTarget();
|
||||
|
||||
QFileInfo fi(m_res);
|
||||
if(!fi.exists()) {
|
||||
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("指定的文件或目录不存在!"), m_res));
|
||||
return;
|
||||
}
|
||||
|
||||
if(fi.isFile()) {
|
||||
m_data_path = fi.path();
|
||||
}
|
||||
else if(fi.isDir()) {
|
||||
m_data_path = m_res;
|
||||
}
|
||||
|
||||
m_data_path = QDir::toNativeSeparators(m_data_path);
|
||||
}
|
||||
|
||||
// 到这里,.tpr和.tpk文件均已经下载完成了。
|
||||
|
||||
if(!_load_header())
|
||||
return;
|
||||
|
||||
if(!_load_keyframe())
|
||||
return;
|
||||
|
||||
|
||||
UpdateData* dat = new UpdateData(m_hdr);
|
||||
emit signal_update_data(dat);
|
||||
|
||||
|
||||
QFile* fdata = nullptr;
|
||||
qint64 file_size = 0;
|
||||
qint64 file_processed = 0;
|
||||
qint64 read_len = 0;
|
||||
QString str_fidx;
|
||||
|
||||
for(;;) {
|
||||
// 任何时候确保第一时间响应退出操作
|
||||
if(m_need_stop)
|
||||
return;
|
||||
|
||||
if(m_need_restart) {
|
||||
if(fdata) {
|
||||
fdata->close();
|
||||
delete fdata;
|
||||
fdata = nullptr;
|
||||
}
|
||||
|
||||
m_wait_restart = true;
|
||||
msleep(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果所有文件都已经处理完了,则等待(可能用户会拖动滚动条,或者重新播放)
|
||||
if(m_file_idx >= m_hdr.info.dat_file_count) {
|
||||
msleep(500);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 看看待播放队列中还有多少个数据包
|
||||
int pkg_count_in_queue = 0;
|
||||
int pkg_need_add = 0;
|
||||
|
||||
m_locker.lock();
|
||||
pkg_count_in_queue = m_data.size();
|
||||
m_locker.unlock();
|
||||
|
||||
// 少于1000个的话,补足到2000个
|
||||
if(m_data.size() < 1000)
|
||||
pkg_need_add = 2000 - pkg_count_in_queue;
|
||||
|
||||
if(pkg_need_add == 0) {
|
||||
msleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
for(int i = 0; i < pkg_need_add; ++i) {
|
||||
if(m_need_stop)
|
||||
return;
|
||||
if(m_need_restart)
|
||||
break;
|
||||
|
||||
// 如果数据文件尚未打开,则打开它
|
||||
if(fdata == nullptr) {
|
||||
str_fidx.sprintf("%d", m_file_idx+1);
|
||||
QString tpd_fname = QString("%1/tp-rdp-%2.tpd").arg(m_data_path, str_fidx);
|
||||
tpd_fname = QDir::toNativeSeparators(tpd_fname);
|
||||
|
||||
QFileInfo fi_tpd(tpd_fname);
|
||||
if(!fi_tpd.exists()) {
|
||||
if(m_need_download) {
|
||||
// 此文件尚未下载完成,等待
|
||||
for(;;) {
|
||||
if(m_need_stop)
|
||||
return;
|
||||
if(!m_thr_download.is_running() || m_thr_download.is_tpd_downloaded(m_file_idx))
|
||||
break;
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
// 下载失败了
|
||||
if(!m_thr_download.is_tpd_downloaded(m_file_idx))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fdata = new QFile(tpd_fname);
|
||||
if(!fdata->open(QFile::ReadOnly)) {
|
||||
qDebug() << "Can not open " << tpd_fname << " for read.";
|
||||
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法打开录像数据文件!"), tpd_fname));
|
||||
return;
|
||||
}
|
||||
|
||||
file_size = fdata->size();
|
||||
file_processed = 0;
|
||||
qDebug("Open file tp-rdp-%d.tpd, processed: %" PRId64 ", size: %" PRId64, m_file_idx+1, file_processed, file_size);
|
||||
}
|
||||
|
||||
// 如果指定了起始偏移,则跳过这部分数据
|
||||
if(m_offset > 0) {
|
||||
fdata->seek(m_offset);
|
||||
file_processed = m_offset;
|
||||
m_offset = 0;
|
||||
}
|
||||
|
||||
//----------------------------------
|
||||
// 读取一个数据包
|
||||
//----------------------------------
|
||||
if(file_size - file_processed < sizeof(TS_RECORD_PKG)) {
|
||||
qDebug("invaid tp-rdp-%d.tpd file, filesize=%" PRId64 ", processed=%" PRId64 ", need=%d.", m_file_idx+1, file_size, file_processed, sizeof(TS_RECORD_PKG));
|
||||
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
|
||||
return;
|
||||
}
|
||||
|
||||
TS_RECORD_PKG pkg;
|
||||
read_len = fdata->read(reinterpret_cast<char*>(&pkg), sizeof(TS_RECORD_PKG));
|
||||
if(read_len != sizeof(TS_RECORD_PKG)) {
|
||||
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (1).", m_file_idx+1, read_len);
|
||||
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
|
||||
return;
|
||||
}
|
||||
file_processed += sizeof(TS_RECORD_PKG);
|
||||
|
||||
if(file_size - file_processed < pkg.size) {
|
||||
qDebug("invaid tp-rdp-%d.tpd file (2).", m_file_idx+1);
|
||||
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
|
||||
return;
|
||||
}
|
||||
|
||||
if(pkg.size == 0) {
|
||||
qDebug("################## too bad.");
|
||||
}
|
||||
|
||||
QByteArray pkg_data = fdata->read(pkg.size);
|
||||
if(pkg_data.size() != static_cast<int>(pkg.size)) {
|
||||
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (3).", m_file_idx+1, read_len);
|
||||
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
|
||||
return;
|
||||
}
|
||||
file_processed += pkg.size;
|
||||
|
||||
UpdateData* dat = _parse(pkg, pkg_data);
|
||||
if(dat == nullptr) {
|
||||
qDebug("invaid tp-rdp-%d.tpd file (4).", m_file_idx+1);
|
||||
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
|
||||
return;
|
||||
}
|
||||
|
||||
// 遇到关键帧,需要清除自上一个关键帧以来保存的缓存图像数据
|
||||
if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
|
||||
for(size_t ci = 0; ci < m_cache_imgs.size(); ++ci) {
|
||||
if(m_cache_imgs[ci] != nullptr)
|
||||
delete m_cache_imgs[ci];
|
||||
}
|
||||
m_cache_imgs.clear();
|
||||
}
|
||||
|
||||
// 拖动滚动条后,需要显示一次关键帧数据,然后跳过后续关键帧。
|
||||
if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
|
||||
qDebug("----key frame: %ld, processed=%" PRId64 ", pkg.size=%d", pkg.time_ms, file_processed, pkg.size);
|
||||
if(m_need_show_kf) {
|
||||
m_need_show_kf = false;
|
||||
qDebug("++ show keyframe.");
|
||||
}
|
||||
else {
|
||||
qDebug("-- skip keyframe.");
|
||||
delete dat;
|
||||
dat = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 数据放到待播放列表中
|
||||
if(dat) {
|
||||
m_locker.lock();
|
||||
m_data.enqueue(dat);
|
||||
m_locker.unlock();
|
||||
}
|
||||
|
||||
// 让线程调度器让播放线程有机会执行
|
||||
// msleep(1);
|
||||
|
||||
// 如果此文件已经处理完毕,则关闭文件,这样下次处理一个新的文件
|
||||
if(file_processed >= file_size) {
|
||||
fdata->close();
|
||||
delete fdata;
|
||||
fdata = nullptr;
|
||||
m_file_idx++;
|
||||
}
|
||||
|
||||
if(m_file_idx >= m_hdr.info.dat_file_count) {
|
||||
UpdateData* dat = new UpdateData(TYPE_END);
|
||||
m_locker.lock();
|
||||
m_data.enqueue(dat);
|
||||
m_locker.unlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateData* ThrData::_parse(const TS_RECORD_PKG& pkg, const QByteArray& data) {
|
||||
if(pkg.type == TS_RECORD_TYPE_RDP_POINTER) {
|
||||
if(data.size() != sizeof(TS_RECORD_RDP_POINTER))
|
||||
return nullptr;
|
||||
|
||||
UpdateData* ud = new UpdateData();
|
||||
ud->set_pointer(pkg.time_ms, reinterpret_cast<const TS_RECORD_RDP_POINTER*>(data.data()));
|
||||
return ud;
|
||||
}
|
||||
else if(pkg.type == TS_RECORD_TYPE_RDP_IMAGE) {
|
||||
UpdateData* ud = new UpdateData(TYPE_IMAGE, pkg.time_ms);
|
||||
|
||||
if(data.size() < static_cast<int>(sizeof(uint16_t) + sizeof(TS_RECORD_RDP_IMAGE_INFO))) {
|
||||
delete ud;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* dat_ptr = reinterpret_cast<const uint8_t*>(data.data());
|
||||
|
||||
uint16_t count = (reinterpret_cast<const uint16_t*>(dat_ptr))[0];
|
||||
uint32_t offset = sizeof(uint16_t);
|
||||
|
||||
UpdateImages& imgs = ud->get_images();
|
||||
|
||||
for(uint16_t i = 0; i < count; ++i) {
|
||||
|
||||
const TS_RECORD_RDP_IMAGE_INFO* info = reinterpret_cast<const TS_RECORD_RDP_IMAGE_INFO*>(dat_ptr+offset);
|
||||
offset += sizeof(TS_RECORD_RDP_IMAGE_INFO);
|
||||
|
||||
if(info->format != TS_RDP_IMG_ALT) {
|
||||
const uint8_t* img_dat = dat_ptr + offset;
|
||||
|
||||
const uint8_t* real_img_dat = nullptr;
|
||||
QByteArray unzip_data;
|
||||
if(info->zip_len > 0) {
|
||||
// 数据被压缩了,需要解压缩
|
||||
unzip_data.resize(static_cast<int>(info->dat_len));
|
||||
|
||||
uLong u_len = info->dat_len;
|
||||
int err = uncompress(reinterpret_cast<uint8_t*>(unzip_data.data()), &u_len, img_dat, info->zip_len);
|
||||
if(err != Z_OK || u_len != info->dat_len) {
|
||||
qDebug("image uncompress failed. err=%d.", err);
|
||||
}
|
||||
else {
|
||||
real_img_dat = reinterpret_cast<const uint8_t*>(unzip_data.data());
|
||||
}
|
||||
|
||||
offset += info->zip_len;
|
||||
}
|
||||
else {
|
||||
real_img_dat = img_dat;
|
||||
offset += info->dat_len;
|
||||
}
|
||||
|
||||
|
||||
UPDATE_IMAGE uimg;
|
||||
uimg.x = info->destLeft;
|
||||
uimg.y = info->destTop;
|
||||
uimg.w = info->destRight - info->destLeft + 1;
|
||||
uimg.h = info->destBottom - info->destTop + 1;
|
||||
if(real_img_dat)
|
||||
uimg.img = _rdpimg2QImage(info->width, info->height, info->bitsPerPixel, (info->format == TS_RDP_IMG_BMP) ? true : false, real_img_dat, info->dat_len);
|
||||
else
|
||||
uimg.img = nullptr;
|
||||
imgs.push_back(uimg);
|
||||
|
||||
QImage* cache_img = nullptr;
|
||||
if(uimg.img != nullptr)
|
||||
cache_img = new QImage(*uimg.img);
|
||||
|
||||
m_cache_imgs.push_back(cache_img);
|
||||
}
|
||||
else {
|
||||
UPDATE_IMAGE uimg;
|
||||
uimg.x = info->destLeft;
|
||||
uimg.y = info->destTop;
|
||||
uimg.w = info->destRight - info->destLeft + 1;
|
||||
uimg.h = info->destBottom - info->destTop + 1;
|
||||
|
||||
size_t cache_idx = info->dat_len;
|
||||
|
||||
if(cache_idx >= m_cache_imgs.size() || m_cache_imgs[cache_idx] == nullptr) {
|
||||
uimg.img = nullptr;
|
||||
}
|
||||
else {
|
||||
uimg.img = new QImage(*m_cache_imgs[cache_idx]);
|
||||
}
|
||||
imgs.push_back(uimg);
|
||||
}
|
||||
}
|
||||
|
||||
return ud;
|
||||
}
|
||||
else if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
|
||||
UpdateData* ud = new UpdateData(TYPE_IMAGE, pkg.time_ms);
|
||||
const TS_RECORD_RDP_KEYFRAME_INFO* info = reinterpret_cast<const TS_RECORD_RDP_KEYFRAME_INFO*>(data.data());
|
||||
const uint8_t* data_buf = reinterpret_cast<const uint8_t*>(data.data() + sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
uint32_t data_len = data.size() - sizeof(TS_RECORD_RDP_KEYFRAME_INFO);
|
||||
|
||||
UpdateImages& imgs = ud->get_images();
|
||||
|
||||
UPDATE_IMAGE uimg;
|
||||
uimg.x = 0;
|
||||
uimg.y = 0;
|
||||
uimg.w = m_hdr.basic.width;
|
||||
uimg.h = m_hdr.basic.height;
|
||||
|
||||
const uint8_t* real_img_dat = nullptr;
|
||||
uint32_t real_img_len = m_hdr.basic.width * m_hdr.basic.height * 2;
|
||||
|
||||
QByteArray unzip_data;
|
||||
if(data_len != real_img_len) {
|
||||
// 数据被压缩了,需要解压缩
|
||||
unzip_data.resize(static_cast<int>(real_img_len));
|
||||
|
||||
uLong u_len = real_img_len;
|
||||
int err = uncompress(reinterpret_cast<uint8_t*>(unzip_data.data()), &u_len, data_buf, data_len);
|
||||
if(err != Z_OK || u_len != real_img_len) {
|
||||
qDebug("keyframe uncompress failed. err=%d.", err);
|
||||
}
|
||||
else {
|
||||
real_img_dat = reinterpret_cast<const uint8_t*>(unzip_data.data());
|
||||
}
|
||||
}
|
||||
else {
|
||||
real_img_dat = data_buf;
|
||||
}
|
||||
|
||||
if(real_img_dat != nullptr)
|
||||
uimg.img = _raw2QImage(m_hdr.basic.width, m_hdr.basic.height, real_img_dat, real_img_len);
|
||||
else
|
||||
uimg.img = nullptr;
|
||||
imgs.push_back(uimg);
|
||||
|
||||
return ud;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void ThrData::restart(uint32_t start_ms) {
|
||||
qDebug("restart at %ld ms", start_ms);
|
||||
// 让处理线程处理完当前循环,然后等待
|
||||
m_need_restart = true;
|
||||
|
||||
// 确保处理线程已经处理完当前循环
|
||||
for(;;) {
|
||||
msleep(50);
|
||||
if(m_need_stop)
|
||||
return;
|
||||
if(m_wait_restart)
|
||||
break;
|
||||
}
|
||||
|
||||
// 清空待播放队列
|
||||
_clear_data();
|
||||
|
||||
if(start_ms == 0) {
|
||||
m_offset = 0;
|
||||
m_file_idx = 0;
|
||||
m_need_show_kf = false;
|
||||
}
|
||||
else {
|
||||
// 找到最接近 start_ms 但小于它的关键帧
|
||||
size_t i = 0;
|
||||
for(i = 0; i < m_kf.size(); ++i) {
|
||||
if(m_kf[i].time_ms > start_ms) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i > 0)
|
||||
i--;
|
||||
|
||||
qDebug("restart acturelly at %ld ms, kf: %d", m_kf[i].time_ms, i);
|
||||
|
||||
// 指定要播放的数据的开始位置
|
||||
m_offset = m_kf[i].offset;
|
||||
m_file_idx = m_kf[i].file_index;
|
||||
if(m_file_idx == (uint32_t)-1)
|
||||
m_file_idx = 0;
|
||||
m_need_show_kf = true;
|
||||
}
|
||||
|
||||
qDebug("RESTART: offset=%d, file_idx=%d", m_offset, m_file_idx);
|
||||
|
||||
// 让处理线程继续
|
||||
m_wait_restart = false;
|
||||
m_need_restart = false;
|
||||
}
|
||||
|
||||
bool ThrData::_load_header() {
|
||||
QString msg;
|
||||
qDebug() << "PATH_BASE: " << m_data_path;
|
||||
|
||||
QString filename = QString("%1/tp-rdp.tpr").arg(m_data_path);
|
||||
filename = QDir::toNativeSeparators(filename);
|
||||
qDebug() << "TPR: " << filename;
|
||||
|
||||
QFile f(filename);
|
||||
if(!f.open(QFile::ReadOnly)) {
|
||||
qDebug() << "Can not open " << filename << " for read.";
|
||||
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法打开录像信息文件!"), filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&m_hdr, 0, sizeof(TS_RECORD_HEADER));
|
||||
|
||||
qint64 read_len = 0;
|
||||
read_len = f.read(reinterpret_cast<char*>(&m_hdr), sizeof(TS_RECORD_HEADER));
|
||||
if(read_len != sizeof(TS_RECORD_HEADER)) {
|
||||
qDebug() << "invaid .tpr file.";
|
||||
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("错误的录像信息文件!"), filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(m_hdr.info.ver != 4) {
|
||||
qDebug() << "invaid .tpr file.";
|
||||
_notify_error(QString("%1 %2%3").arg(LOCAL8BIT("不支持的录像文件版本 "), QString(m_hdr.info.ver), LOCAL8BIT("!\n\n此播放器支持录像文件版本 4。")));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(m_hdr.basic.width == 0 || m_hdr.basic.height == 0) {
|
||||
_notify_error(LOCAL8BIT("错误的录像信息,未记录窗口尺寸!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(m_hdr.info.dat_file_count == 0) {
|
||||
_notify_error(LOCAL8BIT("错误的录像信息,未记录数据文件数量!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThrData::_load_keyframe() {
|
||||
QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_data_path);
|
||||
tpk_fname = QDir::toNativeSeparators(tpk_fname);
|
||||
|
||||
qDebug() << "TPK: " << tpk_fname;
|
||||
|
||||
QFile f_kf(tpk_fname);
|
||||
if(!f_kf.open(QFile::ReadOnly)) {
|
||||
qDebug() << "Can not open " << tpk_fname << " for read.";
|
||||
_notify_error(QString("%1\n\n%3").arg(LOCAL8BIT("无法打开关键帧信息文件!"), tpk_fname));
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 fsize = f_kf.size();
|
||||
if(!fsize || fsize % sizeof(TS_RECORD_RDP_KEYFRAME_INFO) != 0) {
|
||||
qDebug() << "Can not open " << tpk_fname << " for read.";
|
||||
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 read_len = 0;
|
||||
int kf_count = static_cast<int>(fsize / sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
for(int i = 0; i < kf_count; ++i) {
|
||||
TS_RECORD_RDP_KEYFRAME_INFO kf;
|
||||
memset(&kf, 0, sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
read_len = f_kf.read(reinterpret_cast<char*>(&kf), sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
|
||||
if(read_len != sizeof(TS_RECORD_RDP_KEYFRAME_INFO)) {
|
||||
qDebug() << "invaid .tpk file.";
|
||||
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_kf.push_back(kf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UpdateData* ThrData::get_data() {
|
||||
UpdateData* d = nullptr;
|
||||
|
||||
m_locker.lock();
|
||||
if(m_data.size() > 0) {
|
||||
d = m_data.dequeue();
|
||||
}
|
||||
m_locker.unlock();
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
void ThrData::_clear_data() {
|
||||
m_locker.lock();
|
||||
while(m_data.size() > 0) {
|
||||
UpdateData* d = m_data.dequeue();
|
||||
delete d;
|
||||
}
|
||||
m_locker.unlock();
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
#ifndef THR_DATA_H
|
||||
#define THR_DATA_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QQueue>
|
||||
#include <QMutex>
|
||||
#include <QNetworkReply>
|
||||
#include <QFile>
|
||||
#include <QEventLoop>
|
||||
#include <QImage>
|
||||
#include "update_data.h"
|
||||
#include "record_format.h"
|
||||
#include "thr_download.h"
|
||||
|
||||
/*
|
||||
为支持“边下载,边播放”、“可拖动进度条”等功能,录像数据会分为多个文件存放,目前每个文件约4MB。
|
||||
例如:
|
||||
tp-rdp.tpr
|
||||
tp-rdp.tpk (关键帧信息文件,v3.5.1开始引入)
|
||||
tp-rdp-1.tpd, tp-rdp-2.tpd, tp-rdp-3.tpd, ...
|
||||
这样,下载完一个数据文件,即可播放此数据文件中的内容,同时下载线程可以下载后续数据文件。
|
||||
|
||||
为支持“拖动进度条”,可以在数据文件中插入关键帧的方式,这就要求记录录像数据的同时对图像数据进行解码,
|
||||
并同步合成全屏数据(关键帧),每经过一段时间(或者一定数量的图像数据包)之后,就在录像数据文件中增加一个关键帧。
|
||||
正常播放时,跳过此关键帧。
|
||||
当进度条拖放发生时,找到目标时间点之前的最后一个关键帧,从此处开始无延时播放到目标时间点,然后正常播放。
|
||||
因此,需要能够快速定位到各个关键帧,因为有可能此时尚未下载这个关键帧所在的数据文件。定位到此关键帧
|
||||
所在的数据文件后,下载线程要放弃当前下载任务(如果不是当前正在下载的数据文件),并开始下载新的数据文件。
|
||||
因此,需要引入新的关键帧信息文件(.tpk),记录各个关键帧数据所在的数据文件序号、偏移、时间点等信息。
|
||||
|
||||
另外,为保证数据文件、关键帧信息文件等下载正确,下载时保存到对应的临时文件中,并记录已下载字节数,下载完成后再改名,如:
|
||||
tp-rdp.tpk.tmp, tp-rdp.tpk.len
|
||||
tp-rdp-1.tpd.tmp, tp-rdp-1.tpd.len, ...
|
||||
这样,下次需要下载指定文件时,如果发现对应的临时文件存在,可以根据已下载字节数,继续下载。
|
||||
*/
|
||||
|
||||
typedef std::vector<TS_RECORD_RDP_KEYFRAME_INFO> KeyFrames;
|
||||
|
||||
typedef std::vector<QImage*> CachedImages;
|
||||
|
||||
class MainWindow;
|
||||
|
||||
// 下载必要的文件,解析文件数据,生成图像数据(QImage*),将数据包放入待显示队列中,等待 ThrPlay 线程使用
|
||||
// 注意,无需将所有数据解析并放入待显示队列,此队列有数量限制(例如1000个),避免过多占用内存
|
||||
class ThrData : public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ThrData(MainWindow* mainwin, const QString& url);
|
||||
~ThrData();
|
||||
|
||||
virtual void run();
|
||||
void stop();
|
||||
|
||||
void restart(uint32_t start_ms); // 重新从指定时间开始播放
|
||||
|
||||
bool have_more_data();
|
||||
|
||||
UpdateData* get_data();
|
||||
|
||||
private:
|
||||
void _run();
|
||||
|
||||
bool _load_header();
|
||||
bool _load_keyframe();
|
||||
|
||||
void _clear_data();
|
||||
// void _prepare();
|
||||
|
||||
UpdateData* _parse(const TS_RECORD_PKG& pkg, const QByteArray& data);
|
||||
|
||||
void _notify_message(const QString& msg);
|
||||
void _notify_error(const QString& err_msg);
|
||||
|
||||
signals:
|
||||
void signal_update_data(UpdateData*);
|
||||
|
||||
private:
|
||||
MainWindow* m_mainwin;
|
||||
QQueue<UpdateData*> m_data;
|
||||
QMutex m_locker;
|
||||
|
||||
ThrDownload m_thr_download;
|
||||
|
||||
bool m_need_stop;
|
||||
|
||||
bool m_need_download;
|
||||
QString m_res;
|
||||
QString m_data_path_base;
|
||||
|
||||
QString m_url_base;
|
||||
QString m_sid;
|
||||
QString m_rid;
|
||||
QString m_data_path;
|
||||
|
||||
TS_RECORD_HEADER m_hdr;
|
||||
KeyFrames m_kf;
|
||||
|
||||
bool m_need_restart;
|
||||
bool m_wait_restart;
|
||||
bool m_need_show_kf;
|
||||
uint32_t m_file_idx;
|
||||
uint32_t m_offset;
|
||||
|
||||
CachedImages m_cache_imgs;
|
||||
};
|
||||
|
||||
#endif // THR_DATA_H
|
|
@ -0,0 +1,292 @@
|
|||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QNetworkCookie>
|
||||
|
||||
#include "thr_download.h"
|
||||
#include "util.h"
|
||||
#include "downloader.h"
|
||||
#include "record_format.h"
|
||||
|
||||
//=================================================================
|
||||
// ThrDownload
|
||||
//=================================================================
|
||||
|
||||
ThrDownload::ThrDownload() {
|
||||
m_need_stop = false;
|
||||
m_have_tpr = false;
|
||||
m_have_tpk = false;
|
||||
m_have_tpd = nullptr;
|
||||
m_need_tpk = false;
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
ThrDownload::~ThrDownload() {
|
||||
if(m_have_tpd)
|
||||
delete[] m_have_tpd;
|
||||
}
|
||||
|
||||
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意,并不直接访问此URI,实际上其并不存在)
|
||||
// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path/}部分)/session-id(用于判断当前授权用户)/录像会话编号
|
||||
|
||||
bool ThrDownload::init(const QString& local_data_path_base, const QString &res) {
|
||||
m_data_path_base = local_data_path_base;
|
||||
|
||||
QString _tmp_res = res.toLower();
|
||||
if(!_tmp_res.startsWith("http")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList _uris = res.split('/');
|
||||
if(_uris.size() < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sid = _uris[_uris.size()-2];
|
||||
m_rid = _uris[_uris.size()-1];
|
||||
m_url_base = res.left(res.length() - m_sid.length() - m_rid.length() - 2);
|
||||
|
||||
if(m_sid.length() == 0 || m_rid.length() == 0 || m_url_base.length() == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ThrDownload::stop() {
|
||||
if(!m_running)
|
||||
return;
|
||||
// if(!isRunning())
|
||||
// return;
|
||||
m_need_stop = true;
|
||||
wait();
|
||||
qDebug("data thread stop() end.");
|
||||
}
|
||||
|
||||
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意,并不直接访问此URI,实际上其并不存在)
|
||||
// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path/}部分)/session-id(用于判断当前授权用户)/录像会话编号
|
||||
|
||||
void ThrDownload::run() {
|
||||
_run();
|
||||
m_running = false;
|
||||
qDebug("ThrDownload thread run() end.");
|
||||
}
|
||||
|
||||
void ThrDownload::_run() {
|
||||
// m_state = statDownloading;
|
||||
|
||||
if(!_download_tpr()) {
|
||||
// m_state = statFailDone;
|
||||
return;
|
||||
}
|
||||
m_have_tpr = true;
|
||||
|
||||
m_have_tpd = new bool[m_tpd_count];
|
||||
for(uint32_t i = 0; i < m_tpd_count; ++i) {
|
||||
m_have_tpd[i] = false;
|
||||
}
|
||||
|
||||
if(m_need_tpk) {
|
||||
if(!_download_tpk()) {
|
||||
// m_state = statFailDone;
|
||||
return;
|
||||
}
|
||||
m_have_tpk = true;
|
||||
}
|
||||
|
||||
uint32_t file_idx = 0;
|
||||
for(;;) {
|
||||
if(m_need_stop)
|
||||
break;
|
||||
QString str_fidx;
|
||||
str_fidx.sprintf("%d", file_idx+1);
|
||||
|
||||
QString tpd_fname = QString("%1/tp-rdp-%2.tpd").arg(m_data_path, str_fidx);
|
||||
tpd_fname = QDir::toNativeSeparators(tpd_fname);
|
||||
|
||||
QString tmp_fname = QString("%1/tp-rdp-%2.tpd.downloading").arg(m_data_path, str_fidx);
|
||||
tmp_fname = QDir::toNativeSeparators(tmp_fname);
|
||||
|
||||
QFileInfo fi_tmp(tmp_fname);
|
||||
if(fi_tmp.isFile()) {
|
||||
QFile::remove(tmp_fname);
|
||||
}
|
||||
|
||||
QFileInfo fi_tpd(tpd_fname);
|
||||
if(!fi_tpd.exists()) {
|
||||
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp-%3.tpd").arg(m_url_base, m_rid, str_fidx);
|
||||
|
||||
qDebug() << "URL : " << url;
|
||||
qDebug() << "TPD : " << tmp_fname;
|
||||
if(!_download_file(url, tmp_fname)) {
|
||||
// m_state = statFailDone;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!QFile::rename(tmp_fname, tpd_fname)) {
|
||||
// m_state = statFailDone;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_have_tpd[file_idx] = true;
|
||||
|
||||
file_idx += 1;
|
||||
if(file_idx >= m_tpd_count)
|
||||
break;
|
||||
}
|
||||
|
||||
// m_state = statSuccessDone;
|
||||
}
|
||||
|
||||
bool ThrDownload::_download_tpr() {
|
||||
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp.tpr").arg(m_url_base, m_rid);
|
||||
QByteArray data;
|
||||
if(!_download_file(url, data))
|
||||
return false;
|
||||
|
||||
if(data.size() != sizeof(TS_RECORD_HEADER)) {
|
||||
qDebug("invalid header data. %d", data.size());
|
||||
m_error = QString(LOCAL8BIT("录像信息文件数据错误!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
TS_RECORD_HEADER* hdr = reinterpret_cast<TS_RECORD_HEADER*>(data.data());
|
||||
// if(hdr->info.ver != 4) {
|
||||
// qDebug() << "invaid .tpr file.";
|
||||
// m_last_error = QString("%1 %2%3").arg(LOCAL8BIT("不支持的录像文件版本 "), QString(hdr->info.ver), LOCAL8BIT("!\n\n此播放器支持录像文件版本 4。"));
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if(m_hdr.basic.width == 0 || m_hdr.basic.height == 0) {
|
||||
// _notify_error(LOCAL8BIT("错误的录像信息,未记录窗口尺寸!"));
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if(m_hdr.info.dat_file_count == 0) {
|
||||
// _notify_error(LOCAL8BIT("错误的录像信息,未记录数据文件数量!"));
|
||||
// return false;
|
||||
// }
|
||||
|
||||
|
||||
// 下载得到的数据应该是一个TS_RECORD_HEADER,解析此数据,生成本地文件路径,并保存之。
|
||||
QDateTime timeUTC;
|
||||
// timeUTC.setTimeSpec(Qt::UTC);
|
||||
// timeUTC.setTime_t(m_hdr.basic.timestamp);
|
||||
timeUTC.setSecsSinceEpoch(hdr->basic.timestamp);
|
||||
QString strUTC = timeUTC.toString("yyyyMMdd-hhmmss");
|
||||
|
||||
QString strAcc(hdr->basic.acc_username);
|
||||
int idx = strAcc.indexOf('\\');
|
||||
if(-1 != idx) {
|
||||
QString _domain = strAcc.left(idx);
|
||||
QString _user = strAcc.right(strAcc.length() - idx - 1);
|
||||
strAcc = _user + "@" + _domain;
|
||||
}
|
||||
|
||||
QString strType;
|
||||
if(hdr->info.type == TS_TPPR_TYPE_SSH) {
|
||||
strType = "SSH";
|
||||
}
|
||||
else if(hdr->info.type == TS_TPPR_TYPE_RDP) {
|
||||
strType = "RDP";
|
||||
m_need_tpk = true;
|
||||
}
|
||||
else {
|
||||
strType = "UNKNOWN";
|
||||
}
|
||||
|
||||
// .../record/RDP-211-admin-user@domain-192.168.0.68-20191015-020243
|
||||
m_data_path = QString("%1/%2-%3-%4-%5-%6-%7").arg(m_data_path_base, strType, m_rid, hdr->basic.user_username, strAcc, hdr->basic.host_ip, strUTC);
|
||||
m_data_path = QDir::toNativeSeparators(m_data_path);
|
||||
qDebug() << "PATH_BASE: " << m_data_path;
|
||||
|
||||
QDir dir;
|
||||
dir.mkpath(m_data_path);
|
||||
QFileInfo fi;
|
||||
fi.setFile(m_data_path);
|
||||
if(!fi.isDir()) {
|
||||
qDebug("can not create folder to save downloaded file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
QString filename = QString("%1/tp-rdp.tpr").arg(m_data_path);
|
||||
filename = QDir::toNativeSeparators(filename);
|
||||
qDebug() << "TPR: " << filename;
|
||||
|
||||
QFile f;
|
||||
f.setFileName(filename);
|
||||
if(!f.open(QIODevice::WriteOnly | QFile::Truncate)){
|
||||
qDebug("open file for write failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 written = f.write(reinterpret_cast<const char*>(hdr), sizeof(TS_RECORD_HEADER));
|
||||
f.flush();
|
||||
f.close();
|
||||
|
||||
if(written != sizeof(TS_RECORD_HEADER)) {
|
||||
qDebug("save header file failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tpd_count = hdr->info.dat_file_count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThrDownload::_download_tpk() {
|
||||
QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_data_path);
|
||||
tpk_fname = QDir::toNativeSeparators(tpk_fname);
|
||||
|
||||
QString tmp_fname = QString("%1/tp-rdp.tpk.downloading").arg(m_data_path);
|
||||
tmp_fname = QDir::toNativeSeparators(tmp_fname);
|
||||
|
||||
QFileInfo fi_tmp(tmp_fname);
|
||||
if(fi_tmp.isFile()) {
|
||||
QFile::remove(tmp_fname);
|
||||
}
|
||||
|
||||
QFileInfo fi_tpk(tpk_fname);
|
||||
if(!fi_tpk.exists()) {
|
||||
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp.tpk").arg(m_url_base, m_rid);
|
||||
qDebug() << "TPK: " << tmp_fname;
|
||||
if(!_download_file(url, tmp_fname))
|
||||
return false;
|
||||
|
||||
if(!QFile::rename(tmp_fname, tpk_fname))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThrDownload::_download_file(const QString& url, const QString filename) {
|
||||
Downloader dl;
|
||||
if(!dl.request(url, m_sid, filename)) {
|
||||
qDebug() << "download failed.";
|
||||
m_error = QString("%1").arg(LOCAL8BIT("下载文件失败!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThrDownload::_download_file(const QString& url, QByteArray& data) {
|
||||
Downloader dl;
|
||||
if(!dl.request(url, m_sid, &data)) {
|
||||
qDebug() << "download failed.";
|
||||
m_error = QString("%1").arg(LOCAL8BIT("下载文件失败!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThrDownload::is_tpd_downloaded(uint32_t file_idx) const {
|
||||
if(!m_have_tpd)
|
||||
return false;
|
||||
if(file_idx >= m_tpd_count)
|
||||
return false;
|
||||
return m_have_tpd[file_idx];
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef THR_DOWNLOAD_H
|
||||
#define THR_DOWNLOAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QNetworkReply>
|
||||
#include <QFile>
|
||||
#include <QEventLoop>
|
||||
|
||||
class ThrDownload : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
//public:
|
||||
// enum State {
|
||||
// statStarting,
|
||||
// statDownloading,
|
||||
// statInvalidParam,
|
||||
// statFailDone,
|
||||
// statSuccessDone
|
||||
// };
|
||||
|
||||
public:
|
||||
ThrDownload();
|
||||
~ThrDownload();
|
||||
|
||||
bool init(const QString& local_data_path_base, const QString& res);
|
||||
|
||||
virtual void run();
|
||||
void stop();
|
||||
|
||||
bool is_running() const {return m_running;}
|
||||
|
||||
bool is_tpr_downloaded() const {return m_have_tpr;}
|
||||
bool is_tpk_downloaded() const {return m_have_tpk;}
|
||||
bool is_tpd_downloaded(uint32_t file_idx) const;
|
||||
bool get_data_path(QString& path) const {
|
||||
if(m_data_path.isEmpty())
|
||||
return false;
|
||||
path = m_data_path;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void _run();
|
||||
|
||||
bool _download_tpr();
|
||||
bool _download_tpk();
|
||||
|
||||
bool _download_file(const QString& url, const QString filename);
|
||||
bool _download_file(const QString& url, QByteArray& data);
|
||||
|
||||
private:
|
||||
bool m_need_stop;
|
||||
|
||||
QString m_data_path_base;
|
||||
|
||||
QString m_url_base;
|
||||
QString m_sid;
|
||||
QString m_rid;
|
||||
QString m_data_path;
|
||||
|
||||
bool m_running;
|
||||
bool m_have_tpr;
|
||||
bool m_have_tpk;
|
||||
bool m_need_tpk;
|
||||
|
||||
uint32_t m_tpd_count;
|
||||
bool* m_have_tpd;
|
||||
|
||||
QString m_error;
|
||||
};
|
||||
|
||||
#endif // THR_DOWNLOAD_H
|
|
@ -0,0 +1,182 @@
|
|||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
#include "thr_play.h"
|
||||
#include "thr_data.h"
|
||||
#include "mainwindow.h"
|
||||
#include "record_format.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
/*
|
||||
* 录像播放流程:
|
||||
* - 数据处理线程,该线程负责(下载)文件、解析文件,将数据准备成待播放队列;
|
||||
* + 数据处理线程维护待播放队列,少于500个则填充至1000个,每20ms检查一次队列是否少于500个。
|
||||
* - 播放线程从队列中取出一个数据,判断当前时间是否应该播放此数据,如果应该,则将此数据发送给主UI
|
||||
* + if( 播放速率 * (当前时间 - 播放时间) >= (当前数据包偏移时间 - 上个数据包偏移时间)) 则 播放
|
||||
* + 如选择“跳过无操作时间”,则数据包偏移时间差超过3秒的,视为3秒。
|
||||
*/
|
||||
|
||||
|
||||
ThrPlay::ThrPlay(MainWindow* mainwnd) {
|
||||
m_mainwnd = mainwnd;
|
||||
m_need_stop = false;
|
||||
m_need_pause = false;
|
||||
m_speed = 1;
|
||||
m_skip = false;
|
||||
m_start_ms = 0;
|
||||
}
|
||||
|
||||
ThrPlay::~ThrPlay() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void ThrPlay::stop() {
|
||||
if(!isRunning())
|
||||
return;
|
||||
|
||||
m_need_stop = true;
|
||||
wait();
|
||||
qDebug() << "play-thread end.";
|
||||
}
|
||||
|
||||
void ThrPlay::_notify_message(const QString& msg) {
|
||||
UpdateData* _msg = new UpdateData(TYPE_MESSAGE);
|
||||
_msg->message(msg);
|
||||
emit signal_update_data(_msg);
|
||||
}
|
||||
|
||||
void ThrPlay::_notify_error(const QString& msg) {
|
||||
UpdateData* _msg = new UpdateData(TYPE_ERROR);
|
||||
_msg->message(msg);
|
||||
emit signal_update_data(_msg);
|
||||
}
|
||||
|
||||
void ThrPlay::resume(bool relocate, uint32_t start_ms) {
|
||||
if(relocate) {
|
||||
m_start_ms = start_ms;
|
||||
m_first_run = true;
|
||||
}
|
||||
m_need_pause = false;
|
||||
}
|
||||
|
||||
void ThrPlay::run() {
|
||||
|
||||
ThrData* thr_data = m_mainwnd->get_thr_data();
|
||||
m_first_run = true;
|
||||
uint32_t last_time_ms = 0;
|
||||
uint32_t last_pass_ms = 0;
|
||||
|
||||
UpdateData* dat = nullptr;
|
||||
for(;;) {
|
||||
if(m_need_stop)
|
||||
break;
|
||||
|
||||
// 1. 从ThrData的待播放队列中取出一个数据
|
||||
dat = thr_data->get_data();
|
||||
if(dat == nullptr) {
|
||||
msleep(20);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(m_first_run) {
|
||||
m_first_run = false;
|
||||
_notify_message("");
|
||||
}
|
||||
|
||||
if(m_start_ms > 0) {
|
||||
if(dat->get_time() < m_start_ms) {
|
||||
emit signal_update_data(dat);
|
||||
continue;
|
||||
}
|
||||
last_time_ms = m_start_ms;
|
||||
m_start_ms = 0;
|
||||
UpdateData* _enable = new UpdateData(TYPE_ENABLE_DRAW);
|
||||
emit signal_update_data(_enable);
|
||||
}
|
||||
|
||||
// 2. 根据数据包的信息,等待到播放时间点
|
||||
uint32_t need_wait_ms = 0;
|
||||
uint32_t this_time_ms = dat->get_time();
|
||||
uint32_t this_pass_ms = last_time_ms;
|
||||
if(this_time_ms > 0) {
|
||||
if(this_time_ms >= last_time_ms)
|
||||
need_wait_ms = this_time_ms - last_time_ms;
|
||||
else
|
||||
need_wait_ms = 0;
|
||||
|
||||
if(need_wait_ms > 0) {
|
||||
uint32_t time_wait = 0;
|
||||
|
||||
// 如果设置了跳过无操作区间,将超过1秒的等待时间压缩至1秒。
|
||||
if(m_skip) {
|
||||
if(need_wait_ms > 1000)
|
||||
need_wait_ms = 1000;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
time_wait = need_wait_ms > 10 ? 10 : need_wait_ms;
|
||||
msleep(time_wait);
|
||||
|
||||
if(m_need_pause) {
|
||||
while(m_need_pause) {
|
||||
msleep(50);
|
||||
if(m_need_stop)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_need_stop)
|
||||
break;
|
||||
|
||||
if(m_start_ms > 0) {
|
||||
delete dat;
|
||||
dat = nullptr;
|
||||
UpdateData* _disable = new UpdateData(TYPE_DISABLE_DRAW);
|
||||
msleep(500);
|
||||
emit signal_update_data(_disable);
|
||||
break;
|
||||
}
|
||||
|
||||
time_wait *= m_speed;
|
||||
|
||||
// 如果已经在等待长时间无操作区间内,用户设置了跳过无操作区间,则将超过0.5秒的等待时间压缩至0.5秒。
|
||||
if(m_skip) {
|
||||
if(need_wait_ms > 500)
|
||||
need_wait_ms = 500;
|
||||
}
|
||||
|
||||
this_pass_ms += time_wait;
|
||||
if(this_pass_ms - last_pass_ms > 100) {
|
||||
UpdateData* _passed_ms = new UpdateData(TYPE_PLAYED_MS);
|
||||
_passed_ms->played_ms(this_pass_ms);
|
||||
emit signal_update_data(_passed_ms);
|
||||
last_pass_ms = this_pass_ms;
|
||||
}
|
||||
|
||||
if(need_wait_ms <= time_wait)
|
||||
break;
|
||||
else
|
||||
need_wait_ms -= time_wait;
|
||||
}
|
||||
|
||||
if(m_need_stop)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
last_time_ms = this_time_ms;
|
||||
|
||||
// 3. 将数据包发送给主UI界面进行显示
|
||||
if(dat != nullptr) {
|
||||
if(dat->data_type() == TYPE_END) {
|
||||
_notify_message(LOCAL8BIT("播放结束"));
|
||||
}
|
||||
emit signal_update_data(dat);
|
||||
}
|
||||
}
|
||||
|
||||
if(dat != nullptr)
|
||||
delete dat;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef THR_PLAY_H
|
||||
#define THR_PLAY_H
|
||||
|
||||
#include <QThread>
|
||||
#include "update_data.h"
|
||||
#include "downloader.h"
|
||||
|
||||
class MainWindow;
|
||||
// 根据播放规则,将要播放的图像发送给主UI线程进行显示
|
||||
class ThrPlay : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class ThrData;
|
||||
public:
|
||||
ThrPlay(MainWindow* mainwnd);
|
||||
~ThrPlay();
|
||||
|
||||
virtual void run();
|
||||
void stop();
|
||||
void pause() {m_need_pause = true;}
|
||||
void resume(bool relocate, uint32_t start_ms);
|
||||
void speed(int s) {if(s >= 1 && s <= 16) m_speed = s;}
|
||||
void skip(bool s) {m_skip = s;}
|
||||
|
||||
private:
|
||||
void _notify_message(const QString& msg);
|
||||
void _notify_error(const QString& err_msg);
|
||||
|
||||
signals:
|
||||
void signal_update_data(UpdateData*);
|
||||
|
||||
private:
|
||||
MainWindow* m_mainwnd;
|
||||
bool m_need_stop;
|
||||
bool m_need_pause;
|
||||
int m_speed;
|
||||
bool m_skip;
|
||||
bool m_first_run;
|
||||
uint32_t m_start_ms;
|
||||
};
|
||||
|
||||
#endif // THR_PLAY_H
|
|
@ -0,0 +1,58 @@
|
|||
TEMPLATE = app
|
||||
TARGET = tp-player
|
||||
|
||||
QT += core gui widgets network
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
bar.h \
|
||||
thr_play.h \
|
||||
thr_data.h \
|
||||
update_data.h \
|
||||
record_format.h \
|
||||
rle.h \
|
||||
util.h \
|
||||
downloader.h \
|
||||
thr_download.h
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
bar.cpp \
|
||||
thr_play.cpp \
|
||||
thr_data.cpp \
|
||||
update_data.cpp \
|
||||
rle.c \
|
||||
util.cpp \
|
||||
downloader.cpp \
|
||||
thr_download.cpp
|
||||
|
||||
RESOURCES += \
|
||||
tp-player.qrc
|
||||
|
||||
RC_FILE += \
|
||||
tp-player.rc
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
|
||||
|
||||
win32:CONFIG(release, debug|release): {
|
||||
DEFINES += QT_NO_DEBUG_OUTPUT
|
||||
LIBS += -L$$PWD/../../external/zlib/build/release/ -lzlib
|
||||
DESTDIR = $$PWD/../../out/client/x86/Release
|
||||
}
|
||||
else:win32:CONFIG(debug, debug|release): {
|
||||
LIBS += -L$$PWD/../../external/zlib/build/debug/ -lzlibd
|
||||
DESTDIR = $$PWD/../../out/client/x86/Debug
|
||||
}
|
||||
|
||||
INCLUDEPATH += $$PWD/../../external/zlib
|
||||
INCLUDEPATH += $$PWD/../../external/zlib/build
|
||||
DEPENDPATH += $$PWD/../../external/zlib
|
||||
DEPENDPATH += $$PWD/../../external/zlib/build
|
||||
|
||||
#win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/release/libzlibstatic.a
|
||||
#else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/debug/libzlibstaticd.a
|
||||
#else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/release/zlibstatic.lib
|
||||
#else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/debug/zlibstaticd.lib
|
|
@ -0,0 +1,38 @@
|
|||
<RCC>
|
||||
<qresource prefix="/tp-player">
|
||||
<file>res/bg.png</file>
|
||||
<file>res/cursor.png</file>
|
||||
|
||||
<file>res/bar/bg-left.png</file>
|
||||
<file>res/bar/bg-mid.png</file>
|
||||
<file>res/bar/bg-right.png</file>
|
||||
|
||||
<file>res/bar/btn-normal-left.png</file>
|
||||
<file>res/bar/btn-normal-mid.png</file>
|
||||
<file>res/bar/btn-normal-right.png</file>
|
||||
<file>res/bar/btn-sel-left.png</file>
|
||||
<file>res/bar/btn-sel-mid.png</file>
|
||||
<file>res/bar/btn-sel-right.png</file>
|
||||
<file>res/bar/btn-hover-left.png</file>
|
||||
<file>res/bar/btn-hover-mid.png</file>
|
||||
<file>res/bar/btn-hover-right.png</file>
|
||||
|
||||
<file>res/bar/play-hover.png</file>
|
||||
<file>res/bar/play-normal.png</file>
|
||||
<file>res/bar/pause-hover.png</file>
|
||||
<file>res/bar/pause-normal.png</file>
|
||||
|
||||
<file>res/bar/prgbar-mid.png</file>
|
||||
<file>res/bar/prgbar-right.png</file>
|
||||
<file>res/bar/prgbarh-left.png</file>
|
||||
<file>res/bar/prgbarh-mid.png</file>
|
||||
|
||||
<file>res/bar/prgpt-normal.png</file>
|
||||
<file>res/bar/prgpt-hover.png</file>
|
||||
|
||||
<file>res/bar/chkbox-normal.png</file>
|
||||
<file>res/bar/chkbox-hover.png</file>
|
||||
<file>res/bar/chkbox-sel-normal.png</file>
|
||||
<file>res/bar/chkbox-sel-hover.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,2 @@
|
|||
IDI_ICON1 ICON DISCARDABLE "res\\tp-player.ico"
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
#include "update_data.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
UpdateData::UpdateData() : QObject(nullptr)
|
||||
{
|
||||
_init();
|
||||
}
|
||||
|
||||
UpdateData::UpdateData(int data_type) : QObject(nullptr)
|
||||
{
|
||||
_init();
|
||||
m_data_type = data_type;
|
||||
}
|
||||
|
||||
UpdateData::UpdateData(int data_type, uint32_t time_ms) : QObject(nullptr)
|
||||
{
|
||||
_init();
|
||||
m_data_type = data_type;
|
||||
m_time_ms = time_ms;
|
||||
}
|
||||
|
||||
UpdateData::UpdateData(const TS_RECORD_HEADER& hdr) : QObject(nullptr)
|
||||
{
|
||||
_init();
|
||||
m_data_type = TYPE_HEADER_INFO;
|
||||
m_hdr = new TS_RECORD_HEADER;
|
||||
memcpy(m_hdr, &hdr, sizeof(TS_RECORD_HEADER));
|
||||
}
|
||||
|
||||
void UpdateData::_init() {
|
||||
m_data_type = TYPE_UNKNOWN;
|
||||
m_hdr = nullptr;
|
||||
m_pointer = nullptr;
|
||||
|
||||
m_data_buf = nullptr;
|
||||
m_data_len = 0;
|
||||
m_time_ms = 0;
|
||||
}
|
||||
|
||||
UpdateData::~UpdateData() {
|
||||
if(m_hdr)
|
||||
delete m_hdr;
|
||||
if(m_pointer)
|
||||
delete m_pointer;
|
||||
for(int i = 0; i < m_images.size(); ++i) {
|
||||
delete m_images[i].img;
|
||||
}
|
||||
m_images.clear();
|
||||
|
||||
if(m_data_buf)
|
||||
delete m_data_buf;
|
||||
}
|
||||
|
||||
void UpdateData::set_pointer(uint32_t ts, const TS_RECORD_RDP_POINTER* p) {
|
||||
m_data_type = TYPE_POINTER;
|
||||
m_time_ms = ts;
|
||||
m_pointer = new TS_RECORD_RDP_POINTER;
|
||||
memcpy(m_pointer, p, sizeof(TS_RECORD_RDP_POINTER));
|
||||
}
|
||||
|
||||
void UpdateData::alloc_data(uint32_t len) {
|
||||
if(m_data_buf)
|
||||
delete m_data_buf;
|
||||
|
||||
m_data_buf = new uint8_t[len];
|
||||
memset(m_data_buf, 0, len);
|
||||
m_data_len = len;
|
||||
}
|
||||
|
||||
void UpdateData::attach_data(const uint8_t* dat, uint32_t len) {
|
||||
if(m_data_buf)
|
||||
delete m_data_buf;
|
||||
m_data_buf = new uint8_t[len];
|
||||
memcpy(m_data_buf, dat, len);
|
||||
m_data_len = len;
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
#ifndef UPDATE_DATA_H
|
||||
#define UPDATE_DATA_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include "record_format.h"
|
||||
|
||||
#define TYPE_UNKNOWN 0
|
||||
#define TYPE_HEADER_INFO 1
|
||||
|
||||
#define TYPE_DISABLE_DRAW 5
|
||||
#define TYPE_ENABLE_DRAW 6
|
||||
|
||||
#define TYPE_POINTER 10
|
||||
#define TYPE_IMAGE 11
|
||||
#define TYPE_KEYFRAME 12
|
||||
#define TYPE_PLAYED_MS 20
|
||||
#define TYPE_DOWNLOAD_PERCENT 21
|
||||
#define TYPE_END 50
|
||||
#define TYPE_MESSAGE 90
|
||||
#define TYPE_ERROR 91
|
||||
|
||||
|
||||
typedef struct UPDATE_IMAGE {
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
QImage* img;
|
||||
}UPDATE_IMAGE;
|
||||
|
||||
typedef QVector<UPDATE_IMAGE> UpdateImages;
|
||||
|
||||
class UpdateData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateData();
|
||||
explicit UpdateData(int data_type);
|
||||
explicit UpdateData(int data_type, uint32_t time_ms);
|
||||
explicit UpdateData(const TS_RECORD_HEADER& hdr);
|
||||
virtual ~UpdateData();
|
||||
|
||||
void set_pointer(uint32_t ts, const TS_RECORD_RDP_POINTER* p);
|
||||
|
||||
TS_RECORD_HEADER* get_header() {return m_hdr;}
|
||||
TS_RECORD_RDP_POINTER* get_pointer() {return m_pointer;}
|
||||
UpdateImages& get_images() {return m_images;}
|
||||
const UpdateImages& get_images() const {return m_images;}
|
||||
|
||||
uint32_t get_time() {return m_time_ms;}
|
||||
|
||||
void alloc_data(uint32_t len);
|
||||
void attach_data(const uint8_t* dat, uint32_t len);
|
||||
|
||||
int data_type() const {return m_data_type;}
|
||||
|
||||
uint8_t* data_buf() {return m_data_buf;}
|
||||
uint32_t data_len() const {return m_data_len;}
|
||||
|
||||
void played_ms(uint32_t ms) {m_played_ms = ms;}
|
||||
uint32_t played_ms() {return m_played_ms;}
|
||||
|
||||
void message(const QString& msg) {m_msg = msg;}
|
||||
const QString message(){return m_msg;}
|
||||
|
||||
private:
|
||||
void _init(void);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
private:
|
||||
int m_data_type;
|
||||
uint32_t m_time_ms;
|
||||
uint8_t* m_data_buf;
|
||||
uint32_t m_data_len;
|
||||
uint32_t m_played_ms;
|
||||
QString m_msg;
|
||||
|
||||
// for HEADER
|
||||
TS_RECORD_HEADER* m_hdr;
|
||||
// for POINTER
|
||||
TS_RECORD_RDP_POINTER* m_pointer;
|
||||
// for IMAGE
|
||||
UpdateImages m_images;
|
||||
};
|
||||
|
||||
class UpdateDataHelper {
|
||||
public:
|
||||
UpdateDataHelper(UpdateData* data) {
|
||||
m_data = data;
|
||||
}
|
||||
~UpdateDataHelper() {
|
||||
if(m_data)
|
||||
delete m_data;
|
||||
}
|
||||
|
||||
private:
|
||||
UpdateData* m_data;
|
||||
};
|
||||
|
||||
|
||||
#endif // UPDATE_DATA_H
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef TP_PLAYER_UTIL_H
|
||||
#define TP_PLAYER_UTIL_H
|
||||
|
||||
#include <QTime>
|
||||
|
||||
class TimeUseTest {
|
||||
public:
|
||||
TimeUseTest() {
|
||||
m_used_ms = 0;
|
||||
m_count = 0;
|
||||
}
|
||||
~TimeUseTest() {}
|
||||
|
||||
void begin() {
|
||||
m_time.start();
|
||||
}
|
||||
void end() {
|
||||
m_count++;
|
||||
m_used_ms += m_time.elapsed();
|
||||
}
|
||||
|
||||
uint32_t used() const {return m_used_ms;}
|
||||
uint32_t count() const {return m_count;}
|
||||
|
||||
private:
|
||||
QTime m_time;
|
||||
uint32_t m_used_ms;
|
||||
uint32_t m_count;
|
||||
};
|
||||
|
||||
#define LOCAL8BIT(x) QString::fromLocal8Bit(x)
|
||||
|
||||
#endif // TP_PLAYER_UTIL_H
|
|
@ -17,11 +17,11 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.3.1</string>
|
||||
<string>3.5.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.3.1</string>
|
||||
<string>3.5.1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.productivity</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#ifndef __TS_ASSIST_VER_H__
|
||||
#define __TS_ASSIST_VER_H__
|
||||
|
||||
#define TP_ASSIST_VER L"3.3.1"
|
||||
#define TP_ASSIST_VER L"3.5.1"
|
||||
|
||||
#endif // __TS_ASSIST_VER_H__
|
||||
|
|
|
@ -1 +1,14 @@
|
|||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <ex.h>
|
||||
|
||||
// #ifdef EX_DEBUG
|
||||
// // # pragma comment(lib, "libssl32MTd.lib")
|
||||
// // # pragma comment(lib, "libcrypto32MTd.lib")
|
||||
// #else
|
||||
// # pragma comment(lib, "libssl32MT.lib")
|
||||
// # pragma comment(lib, "libcrypto32MT.lib")
|
||||
// #endif
|
||||
|
||||
# pragma comment(lib, "libssl.lib")
|
||||
# pragma comment(lib, "libcrypto.lib")
|
||||
|
|
|
@ -46,8 +46,8 @@
|
|||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>..\..\out\client\$(PlatformTarget)\$(Configuration)\</OutDir>
|
||||
<IntDir>..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\</IntDir>
|
||||
<IncludePath>C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath)</LibraryPath>
|
||||
<IncludePath>C:\apps\vld\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>C:\apps\vld\lib\Win32;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
|
@ -61,13 +61,14 @@
|
|||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;MG_ENABLE_SSL;_DEBUG;_WINDOWS;_WINSOCK_DEPRECATED_NO_WARNINGS;MG_ENABLE_THREADS;MG_DISABLE_HTTP_DIGEST_AUTH;MG_DISABLE_MQTT;MG_DISABLE_SSI;MG_DISABLE_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\external\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
|
@ -79,7 +80,7 @@
|
|||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;MG_ENABLE_SSL;NDEBUG;_WINDOWS;_WINSOCK_DEPRECATED_NO_WARNINGS;MG_ENABLE_THREADS;MG_DISABLE_HTTP_DIGEST_AUTH;MG_DISABLE_MQTT;MG_DISABLE_SSI;MG_DISABLE_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
@ -87,7 +88,8 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\external\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,266 +1,274 @@
|
|||
#include "stdafx.h"
|
||||
#include "ts_cfg.h"
|
||||
#include "ts_env.h"
|
||||
|
||||
|
||||
TsCfg g_cfg;
|
||||
|
||||
TsCfg::TsCfg()
|
||||
{}
|
||||
|
||||
TsCfg::~TsCfg()
|
||||
{}
|
||||
|
||||
bool TsCfg::init(void) {
|
||||
ex_astr file_content;
|
||||
if (!ex_read_text_file(g_env.m_cfg_file, file_content)) {
|
||||
EXLOGE("can not load config file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_load(file_content))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TsCfg::save(const ex_astr& new_value)
|
||||
{
|
||||
if (!_load(new_value))
|
||||
return false;
|
||||
|
||||
Json::StyledWriter jwriter;
|
||||
ex_astr val = jwriter.write(m_root);
|
||||
|
||||
if (!ex_write_text_file(g_env.m_cfg_file, val)) {
|
||||
EXLOGE("can not save config file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TsCfg::_load(const ex_astr& str_json) {
|
||||
Json::Reader jreader;
|
||||
|
||||
if (!jreader.parse(str_json.c_str(), m_root)) {
|
||||
EXLOGE("can not parse new config data, not in json format? %s\n", jreader.getFormattedErrorMessages().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ex_astr sel_name;
|
||||
size_t i = 0;
|
||||
ex_astr tmp;
|
||||
|
||||
//===================================
|
||||
// check ssh config
|
||||
//===================================
|
||||
|
||||
if (!m_root["ssh"].isObject()) {
|
||||
EXLOGE("invalid config, error 1.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_root["ssh"]["selected"].isString()) {
|
||||
EXLOGE("invalid config, error 2.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sel_name = m_root["ssh"]["selected"].asCString();
|
||||
|
||||
if (!m_root["ssh"]["available"].isArray() || m_root["ssh"]["available"].size() == 0) {
|
||||
EXLOGE("invalid config, error 3.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_root["ssh"]["available"].size(); ++i) {
|
||||
|
||||
if (
|
||||
!m_root["ssh"]["available"][i]["name"].isString()
|
||||
|| !m_root["ssh"]["available"][i]["app"].isString()
|
||||
|| !m_root["ssh"]["available"][i]["cmdline"].isString()
|
||||
) {
|
||||
EXLOGE("invalid config, error 4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_root["ssh"]["available"][i]["display"].isNull()) {
|
||||
m_root["ssh"]["available"][i]["display"] = m_root["ssh"]["available"][i]["name"];
|
||||
}
|
||||
|
||||
if (m_root["ssh"]["available"][i]["name"].asCString() != sel_name)
|
||||
continue;
|
||||
|
||||
tmp = m_root["ssh"]["available"][i]["app"].asCString();
|
||||
ex_astr2wstr(tmp, ssh_app, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["ssh"]["available"][i]["cmdline"].asCString();
|
||||
ex_astr2wstr(tmp, ssh_cmdline, EX_CODEPAGE_UTF8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (ssh_app.length() == 0 || ssh_cmdline.length() == 0) {
|
||||
EXLOGE("invalid config, error 6.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//===================================
|
||||
// check sftp config
|
||||
//===================================
|
||||
|
||||
if (!m_root["scp"].isObject()) {
|
||||
EXLOGE("invalid config, error 1.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_root["scp"]["selected"].isString()) {
|
||||
EXLOGE("invalid config, error 2.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sel_name = m_root["scp"]["selected"].asCString();
|
||||
|
||||
if (!m_root["scp"]["available"].isArray() || m_root["scp"]["available"].size() == 0) {
|
||||
EXLOGE("invalid config, error 3.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_root["scp"]["available"].size(); ++i) {
|
||||
|
||||
if (
|
||||
!m_root["scp"]["available"][i]["name"].isString()
|
||||
|| !m_root["scp"]["available"][i]["app"].isString()
|
||||
|| !m_root["scp"]["available"][i]["cmdline"].isString()
|
||||
) {
|
||||
EXLOGE("invalid config, error 4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_root["scp"]["available"][i]["display"].isNull()) {
|
||||
m_root["scp"]["available"][i]["display"] = m_root["scp"]["available"][i]["name"];
|
||||
}
|
||||
|
||||
if (m_root["scp"]["available"][i]["name"].asCString() != sel_name)
|
||||
continue;
|
||||
|
||||
tmp = m_root["scp"]["available"][i]["app"].asCString();
|
||||
ex_astr2wstr(tmp, scp_app, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["scp"]["available"][i]["cmdline"].asCString();
|
||||
ex_astr2wstr(tmp, scp_cmdline, EX_CODEPAGE_UTF8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (scp_app.length() == 0 || scp_cmdline.length() == 0) {
|
||||
EXLOGE("invalid config, error 6.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//===================================
|
||||
// check telnet config
|
||||
//===================================
|
||||
|
||||
if (!m_root["telnet"].isObject()) {
|
||||
EXLOGE("invalid config, error 1.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_root["telnet"]["selected"].isString()) {
|
||||
EXLOGE("invalid config, error 2.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sel_name = m_root["telnet"]["selected"].asCString();
|
||||
|
||||
if (!m_root["telnet"]["available"].isArray() || m_root["telnet"]["available"].size() == 0) {
|
||||
EXLOGE("invalid config, error 3.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_root["telnet"]["available"].size(); ++i) {
|
||||
|
||||
if (
|
||||
!m_root["telnet"]["available"][i]["name"].isString()
|
||||
|| !m_root["telnet"]["available"][i]["app"].isString()
|
||||
|| !m_root["telnet"]["available"][i]["cmdline"].isString()
|
||||
) {
|
||||
EXLOGE("invalid config, error 4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_root["telnet"]["available"][i]["display"].isNull()) {
|
||||
m_root["telnet"]["available"][i]["display"] = m_root["telnet"]["available"][i]["name"];
|
||||
}
|
||||
|
||||
if (m_root["telnet"]["available"][i]["name"].asCString() != sel_name)
|
||||
continue;
|
||||
|
||||
tmp = m_root["telnet"]["available"][i]["app"].asCString();
|
||||
ex_astr2wstr(tmp, telnet_app, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["telnet"]["available"][i]["cmdline"].asCString();
|
||||
ex_astr2wstr(tmp, telnet_cmdline, EX_CODEPAGE_UTF8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (telnet_app.length() == 0 || telnet_cmdline.length() == 0) {
|
||||
EXLOGE("invalid config, error 6.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//===================================
|
||||
// check rdp config
|
||||
//===================================
|
||||
|
||||
if (!m_root["rdp"].isObject()) {
|
||||
EXLOGE("invalid config, error 1.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_root["rdp"]["selected"].isString()) {
|
||||
EXLOGE("invalid config, error 2.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sel_name = m_root["rdp"]["selected"].asCString();
|
||||
|
||||
if (!m_root["rdp"]["available"].isArray() || m_root["rdp"]["available"].size() == 0) {
|
||||
EXLOGE("invalid config, error 3.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_root["rdp"]["available"].size(); ++i) {
|
||||
|
||||
if (
|
||||
!m_root["rdp"]["available"][i]["name"].isString()
|
||||
|| !m_root["rdp"]["available"][i]["app"].isString()
|
||||
|| !m_root["rdp"]["available"][i]["cmdline"].isString()
|
||||
) {
|
||||
EXLOGE("invalid config, error 4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_root["rdp"]["available"][i]["display"].isNull()) {
|
||||
m_root["rdp"]["available"][i]["display"] = m_root["rdp"]["available"][i]["name"];
|
||||
}
|
||||
|
||||
if (m_root["rdp"]["available"][i]["name"].asCString() != sel_name)
|
||||
continue;
|
||||
|
||||
tmp = m_root["rdp"]["available"][i]["app"].asCString();
|
||||
ex_astr2wstr(tmp, rdp_app, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["rdp"]["available"][i]["cmdline"].asCString();
|
||||
ex_astr2wstr(tmp, rdp_cmdline, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["rdp"]["available"][i]["name"].asCString();
|
||||
ex_astr2wstr(tmp, rdp_name, EX_CODEPAGE_UTF8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (rdp_app.length() == 0 || rdp_cmdline.length() == 0 || rdp_name.length() == 0) {
|
||||
EXLOGE("invalid config, error 6.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#include "stdafx.h"
|
||||
#include "ts_cfg.h"
|
||||
#include "ts_env.h"
|
||||
|
||||
|
||||
TsCfg g_cfg;
|
||||
|
||||
TsCfg::TsCfg()
|
||||
{}
|
||||
|
||||
TsCfg::~TsCfg()
|
||||
{}
|
||||
|
||||
bool TsCfg::init(void) {
|
||||
ex_astr file_content;
|
||||
if (!ex_read_text_file(g_env.m_cfg_file, file_content)) {
|
||||
EXLOGE("can not load config file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_load(file_content))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TsCfg::save(const ex_astr& new_value)
|
||||
{
|
||||
if (!_load(new_value))
|
||||
return false;
|
||||
|
||||
//Json::StyledWriter jwriter;
|
||||
Json::StreamWriterBuilder jwb;
|
||||
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
|
||||
ex_aoss os;
|
||||
jwriter->write(m_root, &os);
|
||||
ex_astr val = os.str();
|
||||
|
||||
if (!ex_write_text_file(g_env.m_cfg_file, val)) {
|
||||
EXLOGE("can not save config file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TsCfg::_load(const ex_astr& str_json) {
|
||||
//Json::Reader jreader;
|
||||
Json::CharReaderBuilder jcrb;
|
||||
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
|
||||
const char *str_json_begin = str_json.c_str();
|
||||
|
||||
ex_astr err;
|
||||
if (!jreader->parse(str_json_begin, str_json_begin + str_json.length(), &m_root, &err)) {
|
||||
EXLOGE("can not parse new config data, not in json format? %s\n", err.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ex_astr sel_name;
|
||||
size_t i = 0;
|
||||
ex_astr tmp;
|
||||
|
||||
//===================================
|
||||
// check ssh config
|
||||
//===================================
|
||||
|
||||
if (!m_root["ssh"].isObject()) {
|
||||
EXLOGE("invalid config, error 1.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_root["ssh"]["selected"].isString()) {
|
||||
EXLOGE("invalid config, error 2.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sel_name = m_root["ssh"]["selected"].asCString();
|
||||
|
||||
if (!m_root["ssh"]["available"].isArray() || m_root["ssh"]["available"].size() == 0) {
|
||||
EXLOGE("invalid config, error 3.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_root["ssh"]["available"].size(); ++i) {
|
||||
|
||||
if (
|
||||
!m_root["ssh"]["available"][i]["name"].isString()
|
||||
|| !m_root["ssh"]["available"][i]["app"].isString()
|
||||
|| !m_root["ssh"]["available"][i]["cmdline"].isString()
|
||||
) {
|
||||
EXLOGE("invalid config, error 4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_root["ssh"]["available"][i]["display"].isNull()) {
|
||||
m_root["ssh"]["available"][i]["display"] = m_root["ssh"]["available"][i]["name"];
|
||||
}
|
||||
|
||||
if (m_root["ssh"]["available"][i]["name"].asCString() != sel_name)
|
||||
continue;
|
||||
|
||||
tmp = m_root["ssh"]["available"][i]["app"].asCString();
|
||||
ex_astr2wstr(tmp, ssh_app, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["ssh"]["available"][i]["cmdline"].asCString();
|
||||
ex_astr2wstr(tmp, ssh_cmdline, EX_CODEPAGE_UTF8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (ssh_app.length() == 0 || ssh_cmdline.length() == 0) {
|
||||
EXLOGE("invalid config, error 6.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//===================================
|
||||
// check sftp config
|
||||
//===================================
|
||||
|
||||
if (!m_root["scp"].isObject()) {
|
||||
EXLOGE("invalid config, error 1.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_root["scp"]["selected"].isString()) {
|
||||
EXLOGE("invalid config, error 2.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sel_name = m_root["scp"]["selected"].asCString();
|
||||
|
||||
if (!m_root["scp"]["available"].isArray() || m_root["scp"]["available"].size() == 0) {
|
||||
EXLOGE("invalid config, error 3.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_root["scp"]["available"].size(); ++i) {
|
||||
|
||||
if (
|
||||
!m_root["scp"]["available"][i]["name"].isString()
|
||||
|| !m_root["scp"]["available"][i]["app"].isString()
|
||||
|| !m_root["scp"]["available"][i]["cmdline"].isString()
|
||||
) {
|
||||
EXLOGE("invalid config, error 4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_root["scp"]["available"][i]["display"].isNull()) {
|
||||
m_root["scp"]["available"][i]["display"] = m_root["scp"]["available"][i]["name"];
|
||||
}
|
||||
|
||||
if (m_root["scp"]["available"][i]["name"].asCString() != sel_name)
|
||||
continue;
|
||||
|
||||
tmp = m_root["scp"]["available"][i]["app"].asCString();
|
||||
ex_astr2wstr(tmp, scp_app, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["scp"]["available"][i]["cmdline"].asCString();
|
||||
ex_astr2wstr(tmp, scp_cmdline, EX_CODEPAGE_UTF8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (scp_app.length() == 0 || scp_cmdline.length() == 0) {
|
||||
EXLOGE("invalid config, error 6.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//===================================
|
||||
// check telnet config
|
||||
//===================================
|
||||
|
||||
if (!m_root["telnet"].isObject()) {
|
||||
EXLOGE("invalid config, error 1.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_root["telnet"]["selected"].isString()) {
|
||||
EXLOGE("invalid config, error 2.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sel_name = m_root["telnet"]["selected"].asCString();
|
||||
|
||||
if (!m_root["telnet"]["available"].isArray() || m_root["telnet"]["available"].size() == 0) {
|
||||
EXLOGE("invalid config, error 3.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_root["telnet"]["available"].size(); ++i) {
|
||||
|
||||
if (
|
||||
!m_root["telnet"]["available"][i]["name"].isString()
|
||||
|| !m_root["telnet"]["available"][i]["app"].isString()
|
||||
|| !m_root["telnet"]["available"][i]["cmdline"].isString()
|
||||
) {
|
||||
EXLOGE("invalid config, error 4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_root["telnet"]["available"][i]["display"].isNull()) {
|
||||
m_root["telnet"]["available"][i]["display"] = m_root["telnet"]["available"][i]["name"];
|
||||
}
|
||||
|
||||
if (m_root["telnet"]["available"][i]["name"].asCString() != sel_name)
|
||||
continue;
|
||||
|
||||
tmp = m_root["telnet"]["available"][i]["app"].asCString();
|
||||
ex_astr2wstr(tmp, telnet_app, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["telnet"]["available"][i]["cmdline"].asCString();
|
||||
ex_astr2wstr(tmp, telnet_cmdline, EX_CODEPAGE_UTF8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (telnet_app.length() == 0 || telnet_cmdline.length() == 0) {
|
||||
EXLOGE("invalid config, error 6.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//===================================
|
||||
// check rdp config
|
||||
//===================================
|
||||
|
||||
if (!m_root["rdp"].isObject()) {
|
||||
EXLOGE("invalid config, error 1.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_root["rdp"]["selected"].isString()) {
|
||||
EXLOGE("invalid config, error 2.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
sel_name = m_root["rdp"]["selected"].asCString();
|
||||
|
||||
if (!m_root["rdp"]["available"].isArray() || m_root["rdp"]["available"].size() == 0) {
|
||||
EXLOGE("invalid config, error 3.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_root["rdp"]["available"].size(); ++i) {
|
||||
|
||||
if (
|
||||
!m_root["rdp"]["available"][i]["name"].isString()
|
||||
|| !m_root["rdp"]["available"][i]["app"].isString()
|
||||
|| !m_root["rdp"]["available"][i]["cmdline"].isString()
|
||||
) {
|
||||
EXLOGE("invalid config, error 4.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_root["rdp"]["available"][i]["display"].isNull()) {
|
||||
m_root["rdp"]["available"][i]["display"] = m_root["rdp"]["available"][i]["name"];
|
||||
}
|
||||
|
||||
if (m_root["rdp"]["available"][i]["name"].asCString() != sel_name)
|
||||
continue;
|
||||
|
||||
tmp = m_root["rdp"]["available"][i]["app"].asCString();
|
||||
ex_astr2wstr(tmp, rdp_app, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["rdp"]["available"][i]["cmdline"].asCString();
|
||||
ex_astr2wstr(tmp, rdp_cmdline, EX_CODEPAGE_UTF8);
|
||||
tmp = m_root["rdp"]["available"][i]["name"].asCString();
|
||||
ex_astr2wstr(tmp, rdp_name, EX_CODEPAGE_UTF8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (rdp_app.length() == 0 || rdp_cmdline.length() == 0 || rdp_name.length() == 0) {
|
||||
EXLOGE("invalid config, error 6.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,71 +1,74 @@
|
|||
#include "stdafx.h"
|
||||
#include "ts_env.h"
|
||||
|
||||
#include <time.h>
|
||||
#ifdef EX_OS_WIN32
|
||||
# include <direct.h>
|
||||
//# include <ShlObj.h>
|
||||
#endif
|
||||
|
||||
TsEnv g_env;
|
||||
|
||||
//=======================================================
|
||||
//
|
||||
//=======================================================
|
||||
|
||||
TsEnv::TsEnv()
|
||||
{}
|
||||
|
||||
TsEnv::~TsEnv()
|
||||
{}
|
||||
|
||||
bool TsEnv::init(void)
|
||||
{
|
||||
if (!ex_exec_file(m_exec_file))
|
||||
return false;
|
||||
|
||||
m_exec_path = m_exec_file;
|
||||
if (!ex_dirname(m_exec_path))
|
||||
return false;
|
||||
|
||||
m_cfg_file = m_exec_path;
|
||||
ex_path_join(m_cfg_file, false, L"cfg", L"tp-assist.json", NULL);
|
||||
|
||||
m_log_path = m_exec_path;
|
||||
ex_path_join(m_log_path, false, L"log", NULL);
|
||||
|
||||
ex_wstr cfg_default;
|
||||
|
||||
#ifdef _DEBUG
|
||||
m_site_path = m_exec_path;
|
||||
ex_path_join(m_site_path, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"site", NULL);
|
||||
|
||||
m_tools_path = m_exec_path;
|
||||
ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL);
|
||||
|
||||
cfg_default = m_exec_path;
|
||||
ex_path_join(cfg_default, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"cfg", L"tp-assist.default.json", NULL);
|
||||
|
||||
#else
|
||||
m_site_path = m_exec_path;
|
||||
ex_path_join(m_site_path, false, L"site", NULL);
|
||||
|
||||
m_tools_path = m_exec_path;
|
||||
ex_path_join(m_tools_path, false, L"tools", NULL);
|
||||
|
||||
cfg_default = m_exec_path;
|
||||
ex_path_join(cfg_default, false, L"tp-assist.default.json", NULL);
|
||||
#endif
|
||||
|
||||
if (!ex_is_file_exists(m_cfg_file.c_str())) {
|
||||
ex_wstr cfg_path = m_exec_path;
|
||||
ex_path_join(cfg_path, false, L"cfg", NULL);
|
||||
|
||||
ex_mkdirs(cfg_path);
|
||||
|
||||
if (!ex_copy_file(cfg_default.c_str(), m_cfg_file.c_str()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#include "stdafx.h"
|
||||
#include "ts_env.h"
|
||||
|
||||
#include <time.h>
|
||||
#ifdef EX_OS_WIN32
|
||||
# include <direct.h>
|
||||
//# include <ShlObj.h>
|
||||
#endif
|
||||
|
||||
TsEnv g_env;
|
||||
|
||||
//=======================================================
|
||||
//
|
||||
//=======================================================
|
||||
|
||||
TsEnv::TsEnv()
|
||||
{}
|
||||
|
||||
TsEnv::~TsEnv()
|
||||
{}
|
||||
|
||||
bool TsEnv::init(void)
|
||||
{
|
||||
if (!ex_exec_file(m_exec_file))
|
||||
return false;
|
||||
|
||||
m_exec_path = m_exec_file;
|
||||
if (!ex_dirname(m_exec_path))
|
||||
return false;
|
||||
|
||||
m_cfg_file = m_exec_path;
|
||||
ex_path_join(m_cfg_file, false, L"cfg", L"tp-assist.json", NULL);
|
||||
|
||||
m_log_path = m_exec_path;
|
||||
ex_path_join(m_log_path, false, L"log", NULL);
|
||||
|
||||
ex_wstr cfg_default;
|
||||
|
||||
#ifdef _DEBUG
|
||||
m_site_path = m_exec_path;
|
||||
ex_path_join(m_site_path, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"site", NULL);
|
||||
|
||||
// m_tools_path = m_exec_path;
|
||||
// ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL);
|
||||
|
||||
cfg_default = m_exec_path;
|
||||
ex_path_join(cfg_default, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"cfg", L"tp-assist.default.json", NULL);
|
||||
|
||||
#else
|
||||
m_site_path = m_exec_path;
|
||||
ex_path_join(m_site_path, false, L"site", NULL);
|
||||
|
||||
// m_tools_path = m_exec_path;
|
||||
// ex_path_join(m_tools_path, false, L"tools", NULL);
|
||||
|
||||
cfg_default = m_exec_path;
|
||||
ex_path_join(cfg_default, false, L"tp-assist.default.json", NULL);
|
||||
#endif
|
||||
|
||||
m_tools_path = m_exec_path;
|
||||
ex_path_join(m_tools_path, false, L"tools", NULL);
|
||||
|
||||
if (!ex_is_file_exists(m_cfg_file.c_str())) {
|
||||
ex_wstr cfg_path = m_exec_path;
|
||||
ex_path_join(cfg_path, false, L"cfg", NULL);
|
||||
|
||||
ex_mkdirs(cfg_path);
|
||||
|
||||
if (!ex_copy_file(cfg_default.c_str(), m_cfg_file.c_str()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#ifndef __TS_ASSIST_VER_H__
|
||||
#define __TS_ASSIST_VER_H__
|
||||
|
||||
#define TP_ASSIST_VER L"3.3.1"
|
||||
#define TP_ASSIST_VER L"3.5.1"
|
||||
|
||||
#endif // __TS_ASSIST_VER_H__
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __LIB_EX_H__
|
||||
#ifndef __LIB_EX_H__
|
||||
#define __LIB_EX_H__
|
||||
|
||||
#ifdef EX_HAVE_CONFIG
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __LIB_EX_CONST_H__
|
||||
#ifndef __LIB_EX_CONST_H__
|
||||
#define __LIB_EX_CONST_H__
|
||||
|
||||
#include "ex_platform.h"
|
||||
|
@ -43,8 +43,8 @@
|
|||
// error code.
|
||||
//====================================================
|
||||
#define EXRV_OK 0
|
||||
#define EXRV_SYS_ERR 1 // 系统错误,可以使用GetLastError或者errno来获取具体错误值
|
||||
#define EXRV_FAILED 2 // 操作失败
|
||||
#define EXRV_SYS_ERR 1 // 系统错误,可以使用GetLastError或者errno来获取具体错误值
|
||||
#define EXRV_FAILED 2 // 操作失败
|
||||
|
||||
//#define EXRV_CANNOT_FOUND 9
|
||||
#define EXRV_CANNOT_CREATE 10
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __EX_INI_H__
|
||||
#ifndef __EX_INI_H__
|
||||
#define __EX_INI_H__
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __EX_LOG_H__
|
||||
#ifndef __EX_LOG_H__
|
||||
#define __EX_LOG_H__
|
||||
|
||||
#include "ex_types.h"
|
||||
|
@ -27,7 +27,7 @@ public:
|
|||
|
||||
protected:
|
||||
bool _open_file();
|
||||
bool _rotate_file(void); // 将现有日志文件改名备份,然后新开一个日志文件
|
||||
bool _rotate_file(void); // 将现有日志文件改名备份,然后新开一个日志文件
|
||||
|
||||
public:
|
||||
ExThreadLock lock;
|
||||
|
@ -63,7 +63,7 @@ void EXLOG_USE_LOGGER(ExLogger* logger);
|
|||
void EXLOG_LEVEL(int min_level);
|
||||
void EXLOG_DEBUG(bool debug_mode);
|
||||
|
||||
// 设定日志文件名及路径,如未指定路径,则为可执行程序所在目录下的log目录。
|
||||
// 设定日志文件名及路径,如未指定路径,则为可执行程序所在目录下的log目录。
|
||||
void EXLOG_FILE(const wchar_t* log_file, const wchar_t* log_path = NULL, ex_u32 max_filesize = EX_LOG_FILE_MAX_SIZE, ex_u8 max_filecount = EX_LOG_FILE_MAX_COUNT);
|
||||
|
||||
void EXLOG_CONSOLE(bool output_to_console);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __LIB_EX_PATH_H__
|
||||
#ifndef __LIB_EX_PATH_H__
|
||||
#define __LIB_EX_PATH_H__
|
||||
|
||||
#include "ex_platform.h"
|
||||
|
@ -39,7 +39,7 @@ bool ex_path_join(ex_wstr& inout_path, EX_BOOL auto_abspath, ...);
|
|||
bool ex_abspath_to(const ex_wstr& base_abs_path, const ex_wstr& relate_path, ex_wstr& out_path);
|
||||
bool ex_mkdirs(const ex_wstr& in_path);
|
||||
|
||||
// 获取文件名中的扩展名部分(不包括.,例如abc.py,返回 py)
|
||||
// 获取文件名中的扩展名部分(不包括.,例如abc.py,返回 py)
|
||||
bool ex_path_ext_name(const ex_wstr& in_filename, ex_wstr& out_ext);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __LIB_EX_PLATFORM_H__
|
||||
#ifndef __LIB_EX_PLATFORM_H__
|
||||
#define __LIB_EX_PLATFORM_H__
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
|
|
@ -1,86 +1,89 @@
|
|||
#ifndef __LIB_EX_STR_H__
|
||||
#define __LIB_EX_STR_H__
|
||||
|
||||
#include "ex_types.h"
|
||||
|
||||
#define EX_CODEPAGE_ACP 0
|
||||
#define EX_CODEPAGE_UTF8 1
|
||||
#ifdef EX_OS_WIN32
|
||||
# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_ACP
|
||||
#else
|
||||
# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_UTF8
|
||||
#endif
|
||||
|
||||
#define EX_RSC_BEGIN 0x01
|
||||
#define EX_RSC_END 0x02
|
||||
#define EX_RSC_ALL EX_RSC_BEGIN | EX_RSC_END
|
||||
|
||||
//=================================================
|
||||
// C Interface
|
||||
//=================================================
|
||||
|
||||
// copy a string from `source` to `target`.
|
||||
// `size` is size of target buffer.
|
||||
// if buffer is to small, NULL will return, but `size-1` characters have been copied.
|
||||
char* ex_strcpy(char* target, size_t size, const char* source);
|
||||
wchar_t* ex_wcscpy(wchar_t* target, size_t size, const wchar_t* source);
|
||||
|
||||
|
||||
// dupilicate a string.
|
||||
// must use ex_free() to release the returned value.
|
||||
char* ex_strdup(const char* src);
|
||||
wchar_t* ex_wcsdup(const wchar_t* src);
|
||||
|
||||
// convert between mutli-bytes and wide char string.
|
||||
// must use ex_free() to release the returned value.
|
||||
wchar_t* ex_str2wcs_alloc(const char* in_buffer, int code_page);
|
||||
char* ex_wcs2str_alloc(const wchar_t* in_buffer, int code_page);
|
||||
|
||||
// convert char** argv to wchar_t** argv.
|
||||
// must use ex_free_argv() to release the returned value.
|
||||
wchar_t** ex_make_wargv(int argc, char** argv);
|
||||
void ex_free_wargv(int argc, wchar_t** argv);
|
||||
|
||||
EX_BOOL ex_str_only_white_space(const wchar_t* src);
|
||||
EX_BOOL ex_wcs_only_white_space(const char* src);
|
||||
|
||||
|
||||
int ex_strformat(char* out_buf, size_t buf_size, const char* fmt, ...);
|
||||
int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...);
|
||||
|
||||
//=================================================
|
||||
// C++ Interface
|
||||
//=================================================
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef std::string ex_astr;
|
||||
typedef std::wstring ex_wstr;
|
||||
|
||||
typedef std::vector<ex_astr> ex_astrs;
|
||||
typedef std::vector<ex_wstr> ex_wstrs;
|
||||
typedef std::vector<ex_utf16> ex_str_utf16le;
|
||||
|
||||
bool ex_wstr2astr(const ex_wstr& in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
|
||||
bool ex_wstr2astr(const wchar_t* in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
|
||||
bool ex_astr2wstr(const ex_astr& in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
|
||||
bool ex_astr2wstr(const char* in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
|
||||
|
||||
bool ex_only_white_space(const ex_astr& str_check);
|
||||
bool ex_only_white_space(const ex_wstr& str_check);
|
||||
|
||||
void ex_remove_white_space(ex_astr& str_fix, int ulFlag = EX_RSC_ALL);
|
||||
void ex_remove_white_space(ex_wstr& str_fix, int ulFlag = EX_RSC_ALL);
|
||||
|
||||
ex_astr& ex_replace_all(ex_astr& str, const ex_astr& old_value, const ex_astr& new_value);
|
||||
ex_wstr& ex_replace_all(ex_wstr& str, const ex_wstr& old_value, const ex_wstr& new_value);
|
||||
|
||||
// 将UTF8字符串转换为UTF16-LE字符串(输出结果包含\0结束符)
|
||||
bool ex_utf8_to_utf16le(const std::string& from, ex_str_utf16le& to);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __LIB_EX_STR_H__
|
||||
#ifndef __LIB_EX_STR_H__
|
||||
#define __LIB_EX_STR_H__
|
||||
|
||||
#include "ex_types.h"
|
||||
|
||||
#define EX_CODEPAGE_ACP 0
|
||||
#define EX_CODEPAGE_UTF8 1
|
||||
#ifdef EX_OS_WIN32
|
||||
# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_ACP
|
||||
#else
|
||||
# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_UTF8
|
||||
#endif
|
||||
|
||||
#define EX_RSC_BEGIN 0x01
|
||||
#define EX_RSC_END 0x02
|
||||
#define EX_RSC_ALL EX_RSC_BEGIN | EX_RSC_END
|
||||
|
||||
//=================================================
|
||||
// C Interface
|
||||
//=================================================
|
||||
|
||||
// copy a string from `source` to `target`.
|
||||
// `size` is size of target buffer.
|
||||
// if buffer is to small, NULL will return, but `size-1` characters have been copied.
|
||||
char* ex_strcpy(char* target, size_t size, const char* source);
|
||||
wchar_t* ex_wcscpy(wchar_t* target, size_t size, const wchar_t* source);
|
||||
|
||||
|
||||
// dupilicate a string.
|
||||
// must use ex_free() to release the returned value.
|
||||
char* ex_strdup(const char* src);
|
||||
wchar_t* ex_wcsdup(const wchar_t* src);
|
||||
|
||||
// convert between mutli-bytes and wide char string.
|
||||
// must use ex_free() to release the returned value.
|
||||
wchar_t* ex_str2wcs_alloc(const char* in_buffer, int code_page);
|
||||
char* ex_wcs2str_alloc(const wchar_t* in_buffer, int code_page);
|
||||
|
||||
// convert char** argv to wchar_t** argv.
|
||||
// must use ex_free_argv() to release the returned value.
|
||||
wchar_t** ex_make_wargv(int argc, char** argv);
|
||||
void ex_free_wargv(int argc, wchar_t** argv);
|
||||
|
||||
EX_BOOL ex_str_only_white_space(const wchar_t* src);
|
||||
EX_BOOL ex_wcs_only_white_space(const char* src);
|
||||
|
||||
|
||||
int ex_strformat(char* out_buf, size_t buf_size, const char* fmt, ...);
|
||||
int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...);
|
||||
|
||||
//=================================================
|
||||
// C++ Interface
|
||||
//=================================================
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
|
||||
typedef std::string ex_astr;
|
||||
typedef std::wstring ex_wstr;
|
||||
typedef std::ostringstream ex_aoss;
|
||||
typedef std::wostringstream ex_woss;
|
||||
|
||||
typedef std::vector<ex_astr> ex_astrs;
|
||||
typedef std::vector<ex_wstr> ex_wstrs;
|
||||
typedef std::vector<ex_utf16> ex_str_utf16le;
|
||||
|
||||
bool ex_wstr2astr(const ex_wstr& in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
|
||||
bool ex_wstr2astr(const wchar_t* in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
|
||||
bool ex_astr2wstr(const ex_astr& in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
|
||||
bool ex_astr2wstr(const char* in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
|
||||
|
||||
bool ex_only_white_space(const ex_astr& str_check);
|
||||
bool ex_only_white_space(const ex_wstr& str_check);
|
||||
|
||||
void ex_remove_white_space(ex_astr& str_fix, int ulFlag = EX_RSC_ALL);
|
||||
void ex_remove_white_space(ex_wstr& str_fix, int ulFlag = EX_RSC_ALL);
|
||||
|
||||
ex_astr& ex_replace_all(ex_astr& str, const ex_astr& old_value, const ex_astr& new_value);
|
||||
ex_wstr& ex_replace_all(ex_wstr& str, const ex_wstr& old_value, const ex_wstr& new_value);
|
||||
|
||||
// 将UTF8字符串转换为UTF16-LE字符串(输出结果包含\0结束符)
|
||||
bool ex_utf8_to_utf16le(const std::string& from, ex_str_utf16le& to);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __LIB_EX_STR_H__
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __EX_THREAD_H__
|
||||
#ifndef __EX_THREAD_H__
|
||||
#define __EX_THREAD_H__
|
||||
|
||||
#include "ex_str.h"
|
||||
|
@ -23,11 +23,11 @@ public:
|
|||
|
||||
bool is_running(void) { return m_is_running; }
|
||||
|
||||
// 创建并启动线程(执行被重载了的run()函数)
|
||||
// 创建并启动线程(执行被重载了的run()函数)
|
||||
bool start(void);
|
||||
// 结束线程(等待wait_timeout_ms毫秒,如果wait_timeout_ms为0,则无限等待)
|
||||
// 结束线程(等待wait_timeout_ms毫秒,如果wait_timeout_ms为0,则无限等待)
|
||||
bool stop(void);
|
||||
// 直接结束线程(强杀,不建议使用)
|
||||
// 直接结束线程(强杀,不建议使用)
|
||||
bool terminate(void);
|
||||
|
||||
protected:
|
||||
|
@ -52,7 +52,7 @@ protected:
|
|||
};
|
||||
|
||||
|
||||
// 线程锁(进程内使用)
|
||||
// 线程锁(进程内使用)
|
||||
class ExThreadLock
|
||||
{
|
||||
public:
|
||||
|
@ -70,7 +70,7 @@ private:
|
|||
#endif
|
||||
};
|
||||
|
||||
// 线程锁辅助类
|
||||
// 线程锁辅助类
|
||||
class ExThreadSmartLock
|
||||
{
|
||||
public:
|
||||
|
@ -109,12 +109,12 @@ private:
|
|||
};
|
||||
|
||||
|
||||
// 原子操作
|
||||
// 原子操作
|
||||
int ex_atomic_add(volatile int* pt, int t);
|
||||
int ex_atomic_inc(volatile int* pt);
|
||||
int ex_atomic_dec(volatile int* pt);
|
||||
|
||||
// 线程相关操作
|
||||
// 线程相关操作
|
||||
ex_u64 ex_get_thread_id(void);
|
||||
|
||||
#endif // __EX_THREAD_H__
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
#ifndef __LIB_EX_TYPE_H__
|
||||
#define __LIB_EX_TYPE_H__
|
||||
|
||||
#include "ex_platform.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
typedef signed char ex_i8;
|
||||
typedef signed short ex_i16;
|
||||
|
||||
typedef unsigned char ex_u8;
|
||||
typedef unsigned short ex_u16;
|
||||
typedef unsigned int ex_u32;
|
||||
typedef unsigned long ex_ulong;
|
||||
|
||||
#if defined(EX_OS_WIN32)
|
||||
typedef unsigned __int64 ex_u64;
|
||||
typedef signed __int64 ex_i64;
|
||||
typedef wchar_t ex_utf16;
|
||||
#else
|
||||
typedef unsigned long long ex_u64;
|
||||
typedef signed long long ex_i64;
|
||||
typedef ex_i16 ex_utf16;
|
||||
#endif
|
||||
|
||||
typedef int EX_BOOL;
|
||||
#define EX_TRUE 1
|
||||
#define EX_FALSE 0
|
||||
|
||||
|
||||
typedef std::vector<ex_u8> ex_bin;
|
||||
typedef std::vector<char> ex_chars;
|
||||
|
||||
typedef ex_u32 ex_rv;
|
||||
|
||||
|
||||
#if defined(EX_OS_WIN32)
|
||||
# define EX_DYLIB_HANDLE HINSTANCE
|
||||
#else
|
||||
# define EX_DYLIB_HANDLE void*
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __LIB_EX_TYPE_H__
|
||||
#ifndef __LIB_EX_TYPE_H__
|
||||
#define __LIB_EX_TYPE_H__
|
||||
|
||||
#include "ex_platform.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
typedef signed char ex_i8;
|
||||
typedef signed short ex_i16;
|
||||
|
||||
typedef unsigned char ex_u8;
|
||||
typedef unsigned short ex_u16;
|
||||
typedef unsigned int ex_u32;
|
||||
typedef unsigned long ex_ulong;
|
||||
|
||||
#if defined(EX_OS_WIN32)
|
||||
typedef unsigned __int64 ex_u64;
|
||||
typedef signed __int64 ex_i64;
|
||||
typedef wchar_t ex_utf16;
|
||||
#else
|
||||
typedef unsigned long long ex_u64;
|
||||
typedef signed long long ex_i64;
|
||||
typedef ex_i16 ex_utf16;
|
||||
#endif
|
||||
|
||||
typedef int EX_BOOL;
|
||||
#define EX_TRUE 1
|
||||
#define EX_FALSE 0
|
||||
|
||||
|
||||
typedef std::vector<ex_u8> ex_bin;
|
||||
typedef std::vector<char> ex_chars;
|
||||
|
||||
typedef ex_u32 ex_rv;
|
||||
|
||||
|
||||
#if defined(EX_OS_WIN32)
|
||||
# define EX_DYLIB_HANDLE HINSTANCE
|
||||
#else
|
||||
# define EX_DYLIB_HANDLE void*
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __LIB_EX_TYPE_H__
|
||||
|
|
|
@ -1,55 +1,55 @@
|
|||
#ifndef __LIB_EX_UTIL_H__
|
||||
#define __LIB_EX_UTIL_H__
|
||||
|
||||
#include "ex_types.h"
|
||||
#include "ex_str.h"
|
||||
|
||||
#ifdef EX_OS_WIN32
|
||||
# include <time.h>
|
||||
//# include <io.h>
|
||||
//# include <stdio.h>
|
||||
// #include <direct.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#else
|
||||
// #include <dirent.h>
|
||||
# include <dlfcn.h>
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
EX_BOOL ex_initialize(const char* lc_ctype);
|
||||
|
||||
void ex_free(void* buffer);
|
||||
|
||||
// 在haystack(长度为haystacklen字节)中查找needle(长度为needlelen)的起始地址,返回NULL表示没有找到
|
||||
const ex_u8* ex_memmem(const ex_u8* haystack, size_t haystacklen, const ex_u8* needle, size_t needlelen);
|
||||
void ex_mem_reverse(ex_u8* p, size_t l);
|
||||
|
||||
void ex_printf(const char* fmt, ...);
|
||||
void ex_wprintf(const wchar_t* fmt, ...);
|
||||
|
||||
ex_u64 ex_get_tick_count(void);
|
||||
void ex_sleep_ms(int ms);
|
||||
|
||||
EX_BOOL ex_localtime_now(int* t, struct tm* dt);
|
||||
|
||||
|
||||
FILE* ex_fopen(const ex_wstr& filename, const wchar_t* mode);
|
||||
FILE* ex_fopen(const ex_astr& filename, const char* mode);
|
||||
|
||||
// open a text file and read all content.
|
||||
bool ex_read_text_file(const ex_wstr& file_name, ex_astr& file_content);
|
||||
// open a file and write content.
|
||||
bool ex_write_text_file(const ex_wstr& file_name, const ex_astr& file_content);
|
||||
|
||||
EX_DYLIB_HANDLE ex_dlopen(const wchar_t* dylib_path);
|
||||
void ex_dlclose(EX_DYLIB_HANDLE dylib);
|
||||
|
||||
|
||||
// inet...
|
||||
int ex_ip4_name(const struct sockaddr_in* src, char* dst, size_t size);
|
||||
|
||||
#define EX_IPV4_NAME_LEN 16
|
||||
#define EX_IPV6_NAME_LEN 46
|
||||
const char* ex_inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
|
||||
#endif // __LIB_EX_UTIL_H__
|
||||
#ifndef __LIB_EX_UTIL_H__
|
||||
#define __LIB_EX_UTIL_H__
|
||||
|
||||
#include "ex_types.h"
|
||||
#include "ex_str.h"
|
||||
|
||||
#ifdef EX_OS_WIN32
|
||||
# include <time.h>
|
||||
//# include <io.h>
|
||||
//# include <stdio.h>
|
||||
// #include <direct.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#else
|
||||
// #include <dirent.h>
|
||||
# include <dlfcn.h>
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
EX_BOOL ex_initialize(const char* lc_ctype);
|
||||
|
||||
void ex_free(void* buffer);
|
||||
|
||||
// 在haystack(长度为haystacklen字节)中查找needle(长度为needlelen)的起始地址,返回NULL表示没有找到
|
||||
const ex_u8* ex_memmem(const ex_u8* haystack, size_t haystacklen, const ex_u8* needle, size_t needlelen);
|
||||
void ex_mem_reverse(ex_u8* p, size_t l);
|
||||
|
||||
void ex_printf(const char* fmt, ...);
|
||||
void ex_wprintf(const wchar_t* fmt, ...);
|
||||
|
||||
ex_u64 ex_get_tick_count(void);
|
||||
void ex_sleep_ms(int ms);
|
||||
|
||||
EX_BOOL ex_localtime_now(int* t, struct tm* dt);
|
||||
|
||||
|
||||
FILE* ex_fopen(const ex_wstr& filename, const wchar_t* mode);
|
||||
FILE* ex_fopen(const ex_astr& filename, const char* mode);
|
||||
|
||||
// open a text file and read all content.
|
||||
bool ex_read_text_file(const ex_wstr& file_name, ex_astr& file_content);
|
||||
// open a file and write content.
|
||||
bool ex_write_text_file(const ex_wstr& file_name, const ex_astr& file_content);
|
||||
|
||||
EX_DYLIB_HANDLE ex_dlopen(const wchar_t* dylib_path);
|
||||
void ex_dlclose(EX_DYLIB_HANDLE dylib);
|
||||
|
||||
|
||||
// inet...
|
||||
int ex_ip4_name(const struct sockaddr_in* src, char* dst, size_t size);
|
||||
|
||||
#define EX_IPV4_NAME_LEN 16
|
||||
#define EX_IPV6_NAME_LEN 46
|
||||
const char* ex_inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
|
||||
#endif // __LIB_EX_UTIL_H__
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __EX_WINSRV_H__
|
||||
#ifndef __EX_WINSRV_H__
|
||||
#define __EX_WINSRV_H__
|
||||
|
||||
#include "ex_str.h"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include <ex/ex_ini.h>
|
||||
#include <ex/ex_ini.h>
|
||||
#include <ex/ex_log.h>
|
||||
#include <ex/ex_util.h>
|
||||
|
||||
|
@ -241,7 +241,7 @@ bool ExIniFile::LoadFromFile(const ex_wstr& strFileName, bool bClearOld)
|
|||
{
|
||||
pOffset += 3;
|
||||
}
|
||||
// 配置文件均使用UTF8编码
|
||||
// 配置文件均使用UTF8编码
|
||||
ex_wstr fileData;
|
||||
if (!ex_astr2wstr(pOffset, fileData, EX_CODEPAGE_UTF8))
|
||||
return false;
|
||||
|
@ -357,7 +357,7 @@ void ExIniFile::Save(int codepage/* = EX_CODEPAGE_UTF8*/)
|
|||
return;
|
||||
}
|
||||
|
||||
// 如果有不属于任何小节的值对,先保存之
|
||||
// 如果有不属于任何小节的值对,先保存之
|
||||
if (m_dumy_sec.Count() > 0)
|
||||
m_dumy_sec.Save(file, codepage);
|
||||
|
||||
|
@ -415,16 +415,16 @@ ExIniSection* ExIniFile::GetSection(const ex_wstr& strName, bool bCreateIfNotExi
|
|||
}
|
||||
|
||||
// static function.
|
||||
// 解析一行,返回值为 [节名/值对/注释/什么也不是/出错了]
|
||||
// 节名 => strKey = [section_name]
|
||||
// 值对 => strKey = strValue
|
||||
// 解析一行,返回值为 [节名/值对/注释/什么也不是/出错了]
|
||||
// 节名 => strKey = [section_name]
|
||||
// 值对 => strKey = strValue
|
||||
ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& strKey, ex_wstr& strValue)
|
||||
{
|
||||
// 首先去掉行首的空格或者 TAB 控制
|
||||
// 首先去掉行首的空格或者 TAB 控制
|
||||
ex_wstr strLine(strOrigLine);
|
||||
ex_remove_white_space(strLine, EX_RSC_BEGIN);
|
||||
|
||||
// 判断是否为注释。 .ini 文件以 分号';'/'#' 作为注释行的第一个字符
|
||||
// 判断是否为注释。 .ini 文件以 分号';'/'#' 作为注释行的第一个字符
|
||||
if (';' == strLine[0] || '#' == strLine[0])
|
||||
{
|
||||
return PARSE_COMMENT;
|
||||
|
@ -432,7 +432,7 @@ ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& s
|
|||
|
||||
if ('[' == strLine[0])
|
||||
{
|
||||
// 这是一个节(section)
|
||||
// 这是一个节(section)
|
||||
ex_wstr::size_type startPos = strLine.find('[');
|
||||
ex_wstr::size_type endPos = strLine.rfind(']');
|
||||
strLine.erase(endPos);
|
||||
|
@ -443,23 +443,23 @@ ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& s
|
|||
}
|
||||
else
|
||||
{
|
||||
// 看看能否找到等号(=),这是 key=value 的判别方法
|
||||
// 看看能否找到等号(=),这是 key=value 的判别方法
|
||||
ex_wstr::size_type pos = strLine.find('=');
|
||||
if (ex_wstr::npos == pos)
|
||||
{
|
||||
//return PARSE_OTHER; // 没有等号
|
||||
//return PARSE_OTHER; // 没有等号
|
||||
ex_remove_white_space(strLine);
|
||||
strKey = strLine;
|
||||
strValue.clear();
|
||||
return PARSE_KEYVALUE;
|
||||
}
|
||||
|
||||
// 将等号前面的与等号后面的分割
|
||||
// 将等号前面的与等号后面的分割
|
||||
strKey.assign(strLine, 0, pos);
|
||||
strValue.assign(strLine, pos + 1, strLine.length() - pos);
|
||||
ex_remove_white_space(strKey);
|
||||
|
||||
// 等号后面的应该原封不动,不应该移除空白字符
|
||||
// 等号后面的应该原封不动,不应该移除空白字符
|
||||
ex_remove_white_space(strValue, EX_RSC_BEGIN);
|
||||
|
||||
return PARSE_KEYVALUE;
|
||||
|
@ -489,7 +489,7 @@ bool ExIniFile::_ProcessLine(const ex_wstr strLine, ExIniSection** pCurSection)
|
|||
break;
|
||||
case PARSE_SECTION:
|
||||
{
|
||||
// 创建一个节
|
||||
// 创建一个节
|
||||
ExIniSection* pSection = GetSection(strKey, true);
|
||||
if (NULL == pSection)
|
||||
{
|
||||
|
@ -508,7 +508,7 @@ bool ExIniFile::_ProcessLine(const ex_wstr strLine, ExIniSection** pCurSection)
|
|||
*pCurSection = &m_dumy_sec;
|
||||
}
|
||||
|
||||
// 创建一个值对
|
||||
// 创建一个值对
|
||||
if (!(*pCurSection)->SetValue(strKey, strValue, true))
|
||||
{
|
||||
bError = true;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include <ex/ex_path.h>
|
||||
#include <ex/ex_path.h>
|
||||
#include <ex/ex_const.h>
|
||||
#include <ex/ex_util.h>
|
||||
|
||||
|
|
|
@ -1,224 +1,224 @@
|
|||
#include <ex/ex_thread.h>
|
||||
#include <ex/ex_log.h>
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
//=========================================================
|
||||
|
||||
|
||||
#ifdef EX_OS_WIN32
|
||||
unsigned int WINAPI ExThreadBase::_thread_func(LPVOID pParam)
|
||||
#else
|
||||
|
||||
void *ExThreadBase::_thread_func(void *pParam)
|
||||
#endif
|
||||
{
|
||||
ExThreadBase *_this = (ExThreadBase *) pParam;
|
||||
|
||||
_this->m_is_running = true;
|
||||
_this->_thread_loop();
|
||||
_this->m_is_running = false;
|
||||
_this->m_handle = 0;
|
||||
|
||||
EXLOGV(" # thread [%s] exit.\n", _this->m_thread_name.c_str());
|
||||
_this->_on_stopped();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ExThreadBase::ExThreadBase(const char *thread_name) :
|
||||
m_handle(0),
|
||||
m_is_running(false),
|
||||
m_need_stop(false) {
|
||||
m_thread_name = thread_name;
|
||||
}
|
||||
|
||||
ExThreadBase::~ExThreadBase() {
|
||||
if(m_is_running) {
|
||||
EXLOGE(" # thread [%s] not stop before destroy.\n", m_thread_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool ExThreadBase::start(void) {
|
||||
m_need_stop = false;
|
||||
EXLOGV(" . thread [%s] starting.\n", m_thread_name.c_str());
|
||||
#ifdef WIN32
|
||||
HANDLE h = (HANDLE)_beginthreadex(NULL, 0, _thread_func, (void*)this, 0, NULL);
|
||||
|
||||
if (NULL == h)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_handle = h;
|
||||
#else
|
||||
pthread_t ptid = 0;
|
||||
int ret = pthread_create(&ptid, NULL, _thread_func, (void *) this);
|
||||
if (ret != 0) {
|
||||
return false;
|
||||
}
|
||||
m_handle = ptid;
|
||||
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExThreadBase::stop(void) {
|
||||
if (m_handle == 0) {
|
||||
EXLOGW("[thread] thread [%s] already stopped.\n", m_thread_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
EXLOGV("[thread] try to stop thread [%s].\n", m_thread_name.c_str());
|
||||
m_need_stop = true;
|
||||
_on_stop();
|
||||
|
||||
EXLOGV("[thread] wait thread [%s] exit.\n", m_thread_name.c_str());
|
||||
|
||||
#ifdef EX_OS_WIN32
|
||||
if (WaitForSingleObject(m_handle, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (pthread_join(m_handle, NULL) != 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExThreadBase::terminate(void) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return (TerminateThread(m_handle, 1) == TRUE);
|
||||
#else
|
||||
return (pthread_cancel(m_handle) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
//=========================================================
|
||||
|
||||
ExThreadManager::ExThreadManager() {}
|
||||
|
||||
ExThreadManager::~ExThreadManager() {
|
||||
if (!m_threads.empty()) {
|
||||
EXLOGE("when destroy thread manager, there are %d thread not exit.\n", m_threads.size());
|
||||
stop_all();
|
||||
}
|
||||
}
|
||||
|
||||
void ExThreadManager::stop_all(void) {
|
||||
ExThreadSmartLock locker(m_lock);
|
||||
|
||||
ex_threads::iterator it = m_threads.begin();
|
||||
for (; it != m_threads.end(); ++it) {
|
||||
(*it)->stop();
|
||||
}
|
||||
m_threads.clear();
|
||||
}
|
||||
|
||||
void ExThreadManager::add(ExThreadBase *tb) {
|
||||
ExThreadSmartLock locker(m_lock);
|
||||
|
||||
ex_threads::iterator it = m_threads.begin();
|
||||
for (; it != m_threads.end(); ++it) {
|
||||
if ((*it) == tb) {
|
||||
EXLOGE("when add thread to manager, it already exist.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_threads.push_back(tb);
|
||||
}
|
||||
|
||||
void ExThreadManager::remove(ExThreadBase *tb) {
|
||||
ExThreadSmartLock locker(m_lock);
|
||||
|
||||
ex_threads::iterator it = m_threads.begin();
|
||||
for (; it != m_threads.end(); ++it) {
|
||||
if ((*it) == tb) {
|
||||
m_threads.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXLOGE("thread not hold by thread-manager while remove it.\n");
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
//=========================================================
|
||||
|
||||
ExThreadLock::ExThreadLock() {
|
||||
#ifdef EX_OS_WIN32
|
||||
InitializeCriticalSection(&m_locker);
|
||||
#else
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&m_locker, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
#endif
|
||||
}
|
||||
|
||||
ExThreadLock::~ExThreadLock() {
|
||||
#ifdef EX_OS_WIN32
|
||||
DeleteCriticalSection(&m_locker);
|
||||
#else
|
||||
pthread_mutex_destroy(&m_locker);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ExThreadLock::lock(void) {
|
||||
#ifdef EX_OS_WIN32
|
||||
EnterCriticalSection(&m_locker);
|
||||
#else
|
||||
pthread_mutex_lock(&m_locker);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ExThreadLock::unlock(void) {
|
||||
#ifdef EX_OS_WIN32
|
||||
LeaveCriticalSection(&m_locker);
|
||||
#else
|
||||
pthread_mutex_unlock(&m_locker);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
//=========================================================
|
||||
|
||||
int ex_atomic_add(volatile int *pt, int t) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return (int)InterlockedExchangeAdd((long*)pt, (long)t);
|
||||
#else
|
||||
return __sync_add_and_fetch(pt, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ex_atomic_inc(volatile int *pt) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return (int)InterlockedIncrement((long*)pt);
|
||||
#else
|
||||
return __sync_add_and_fetch(pt, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ex_atomic_dec(volatile int *pt) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return (int)InterlockedDecrement((long*)pt);
|
||||
#else
|
||||
return __sync_add_and_fetch(pt, -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ex_u64 ex_get_thread_id(void) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return (ex_u64) pthread_self();
|
||||
#endif
|
||||
}
|
||||
#include <ex/ex_thread.h>
|
||||
#include <ex/ex_log.h>
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
//=========================================================
|
||||
|
||||
|
||||
#ifdef EX_OS_WIN32
|
||||
unsigned int WINAPI ExThreadBase::_thread_func(LPVOID pParam)
|
||||
#else
|
||||
|
||||
void *ExThreadBase::_thread_func(void *pParam)
|
||||
#endif
|
||||
{
|
||||
ExThreadBase *_this = (ExThreadBase *) pParam;
|
||||
|
||||
_this->m_is_running = true;
|
||||
_this->_thread_loop();
|
||||
_this->m_is_running = false;
|
||||
_this->m_handle = 0;
|
||||
|
||||
EXLOGV(" # thread [%s] exit.\n", _this->m_thread_name.c_str());
|
||||
_this->_on_stopped();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ExThreadBase::ExThreadBase(const char *thread_name) :
|
||||
m_handle(0),
|
||||
m_is_running(false),
|
||||
m_need_stop(false) {
|
||||
m_thread_name = thread_name;
|
||||
}
|
||||
|
||||
ExThreadBase::~ExThreadBase() {
|
||||
if(m_is_running) {
|
||||
EXLOGE(" # thread [%s] not stop before destroy.\n", m_thread_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool ExThreadBase::start(void) {
|
||||
m_need_stop = false;
|
||||
EXLOGV(" . thread [%s] starting.\n", m_thread_name.c_str());
|
||||
#ifdef WIN32
|
||||
HANDLE h = (HANDLE)_beginthreadex(NULL, 0, _thread_func, (void*)this, 0, NULL);
|
||||
|
||||
if (NULL == h)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_handle = h;
|
||||
#else
|
||||
pthread_t ptid = 0;
|
||||
int ret = pthread_create(&ptid, NULL, _thread_func, (void *) this);
|
||||
if (ret != 0) {
|
||||
return false;
|
||||
}
|
||||
m_handle = ptid;
|
||||
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExThreadBase::stop(void) {
|
||||
if (m_handle == 0) {
|
||||
EXLOGW("[thread] thread [%s] already stopped.\n", m_thread_name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
EXLOGV("[thread] try to stop thread [%s].\n", m_thread_name.c_str());
|
||||
m_need_stop = true;
|
||||
_on_stop();
|
||||
|
||||
EXLOGV("[thread] wait thread [%s] exit.\n", m_thread_name.c_str());
|
||||
|
||||
#ifdef EX_OS_WIN32
|
||||
if (WaitForSingleObject(m_handle, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (pthread_join(m_handle, NULL) != 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExThreadBase::terminate(void) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return (TerminateThread(m_handle, 1) == TRUE);
|
||||
#else
|
||||
return (pthread_cancel(m_handle) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
//=========================================================
|
||||
|
||||
ExThreadManager::ExThreadManager() {}
|
||||
|
||||
ExThreadManager::~ExThreadManager() {
|
||||
if (!m_threads.empty()) {
|
||||
EXLOGE("when destroy thread manager, there are %d thread not exit.\n", m_threads.size());
|
||||
stop_all();
|
||||
}
|
||||
}
|
||||
|
||||
void ExThreadManager::stop_all(void) {
|
||||
ExThreadSmartLock locker(m_lock);
|
||||
|
||||
ex_threads::iterator it = m_threads.begin();
|
||||
for (; it != m_threads.end(); ++it) {
|
||||
(*it)->stop();
|
||||
}
|
||||
m_threads.clear();
|
||||
}
|
||||
|
||||
void ExThreadManager::add(ExThreadBase *tb) {
|
||||
ExThreadSmartLock locker(m_lock);
|
||||
|
||||
ex_threads::iterator it = m_threads.begin();
|
||||
for (; it != m_threads.end(); ++it) {
|
||||
if ((*it) == tb) {
|
||||
EXLOGE("when add thread to manager, it already exist.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_threads.push_back(tb);
|
||||
}
|
||||
|
||||
void ExThreadManager::remove(ExThreadBase *tb) {
|
||||
ExThreadSmartLock locker(m_lock);
|
||||
|
||||
ex_threads::iterator it = m_threads.begin();
|
||||
for (; it != m_threads.end(); ++it) {
|
||||
if ((*it) == tb) {
|
||||
m_threads.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXLOGE("thread not hold by thread-manager while remove it.\n");
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
//=========================================================
|
||||
|
||||
ExThreadLock::ExThreadLock() {
|
||||
#ifdef EX_OS_WIN32
|
||||
InitializeCriticalSection(&m_locker);
|
||||
#else
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&m_locker, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
#endif
|
||||
}
|
||||
|
||||
ExThreadLock::~ExThreadLock() {
|
||||
#ifdef EX_OS_WIN32
|
||||
DeleteCriticalSection(&m_locker);
|
||||
#else
|
||||
pthread_mutex_destroy(&m_locker);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ExThreadLock::lock(void) {
|
||||
#ifdef EX_OS_WIN32
|
||||
EnterCriticalSection(&m_locker);
|
||||
#else
|
||||
pthread_mutex_lock(&m_locker);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ExThreadLock::unlock(void) {
|
||||
#ifdef EX_OS_WIN32
|
||||
LeaveCriticalSection(&m_locker);
|
||||
#else
|
||||
pthread_mutex_unlock(&m_locker);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
//=========================================================
|
||||
|
||||
int ex_atomic_add(volatile int *pt, int t) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return (int)InterlockedExchangeAdd((long*)pt, (long)t);
|
||||
#else
|
||||
return __sync_add_and_fetch(pt, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ex_atomic_inc(volatile int *pt) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return (int)InterlockedIncrement((long*)pt);
|
||||
#else
|
||||
return __sync_add_and_fetch(pt, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ex_atomic_dec(volatile int *pt) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return (int)InterlockedDecrement((long*)pt);
|
||||
#else
|
||||
return __sync_add_and_fetch(pt, -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ex_u64 ex_get_thread_id(void) {
|
||||
#ifdef EX_OS_WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return (ex_u64) pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include <ex/ex_platform.h>
|
||||
#include <ex/ex_platform.h>
|
||||
#include <ex/ex_util.h>
|
||||
#include <ex/ex_str.h>
|
||||
#include <ex/ex_log.h>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include <ex/ex_winsrv.h>
|
||||
#include <ex/ex_winsrv.h>
|
||||
|
||||
#ifdef EX_OS_WIN32
|
||||
|
||||
|
@ -44,14 +44,14 @@ ex_rv ex_winsrv_install(const ex_wstr& srv_name, const ex_wstr& disp_name, const
|
|||
}
|
||||
|
||||
SERVICE_FAILURE_ACTIONS failure_action;
|
||||
failure_action.dwResetPeriod = 0; // reset failure count to zero 的时间,单位为秒
|
||||
failure_action.dwResetPeriod = 0; // reset failure count to zero 的时间,单位为秒
|
||||
failure_action.lpRebootMsg = NULL; // Message to broadcast to server users before rebooting
|
||||
failure_action.lpCommand = NULL; // Command line of the process for the CreateProcess function to execute in response
|
||||
failure_action.cActions = 3; // action数组的个数
|
||||
failure_action.cActions = 3; // action数组的个数
|
||||
|
||||
SC_ACTION actionarray[3];
|
||||
actionarray[0].Type = SC_ACTION_RESTART; // 重新启动服务
|
||||
actionarray[0].Delay = 60000; // 单位为毫秒
|
||||
actionarray[0].Type = SC_ACTION_RESTART; // 重新启动服务
|
||||
actionarray[0].Delay = 60000; // 单位为毫秒
|
||||
actionarray[1].Type = SC_ACTION_RESTART;
|
||||
actionarray[1].Delay = 60000;
|
||||
actionarray[2].Type = SC_ACTION_RESTART;
|
||||
|
|
|
@ -1,151 +1,151 @@
|
|||
#ifndef __TELEPORT_CONST_H__
|
||||
#define __TELEPORT_CONST_H__
|
||||
|
||||
// 注意同步更新三个不同语言的const文件
|
||||
|
||||
// 本文件设定teleport各个模块之间通讯时的错误值(JSON数据),包括:
|
||||
// - WEB界面与助手
|
||||
// - WEB界面与WEB后台
|
||||
// - WEB后台与CORE核心服务
|
||||
|
||||
//=======================================================
|
||||
// Urlprotocol相关
|
||||
//=======================================================
|
||||
#define TP_URLPROTO_APP_NAME "teleport"
|
||||
|
||||
//=======================================================
|
||||
// 远程连接认证方式
|
||||
//=======================================================
|
||||
#define TP_AUTH_TYPE_NONE 0
|
||||
#define TP_AUTH_TYPE_PASSWORD 1
|
||||
#define TP_AUTH_TYPE_PRIVATE_KEY 2
|
||||
|
||||
//=======================================================
|
||||
// 远程连接协议
|
||||
//=======================================================
|
||||
#define TP_PROTOCOL_TYPE_RDP 1
|
||||
#define TP_PROTOCOL_TYPE_SSH 2
|
||||
#define TP_PROTOCOL_TYPE_TELNET 3
|
||||
|
||||
//=======================================================
|
||||
// 远程连接子协议
|
||||
//=======================================================
|
||||
#define TP_PROTOCOL_TYPE_RDP_DESKTOP 100
|
||||
#define TP_PROTOCOL_TYPE_SSH_SHELL 200
|
||||
#define TP_PROTOCOL_TYPE_SSH_SFTP 201
|
||||
#define TP_PROTOCOL_TYPE_TELNET_SHELL 300
|
||||
|
||||
|
||||
//=======================================================
|
||||
// 远程主机操作系统
|
||||
//=======================================================
|
||||
#define TP_OS_TYPE_WINDOWS 1
|
||||
#define TP_OS_TYPE_LINUX 2
|
||||
|
||||
//=======================================================
|
||||
// 远程连接会话状态
|
||||
//=======================================================
|
||||
#define TP_SESS_STAT_RUNNING 0 // 会话开始了,正在连接
|
||||
#define TP_SESS_STAT_END 9999 // 会话成功结束
|
||||
#define TP_SESS_STAT_ERR_AUTH_DENIED 1 // 会话结束,因为认证失败
|
||||
#define TP_SESS_STAT_ERR_CONNECT 2 // 会话结束,因为无法连接到远程主机
|
||||
#define TP_SESS_STAT_ERR_BAD_SSH_KEY 3 // 会话结束,因为无法识别SSH私钥
|
||||
#define TP_SESS_STAT_ERR_INTERNAL 4 // 会话结束,因为内部错误
|
||||
#define TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL 5 // 会话结束,因为协议不支持(RDP)
|
||||
#define TP_SESS_STAT_ERR_BAD_PKG 6 // 会话结束,因为收到错误的报文
|
||||
#define TP_SESS_STAT_ERR_RESET 7 // 会话结束,因为teleport核心服务重置了
|
||||
#define TP_SESS_STAT_ERR_IO 8 // 会话结束,因为网络中断
|
||||
#define TP_SESS_STAT_ERR_SESSION 9 // 会话结束,因为无效的会话ID
|
||||
#define TP_SESS_STAT_ERR_AUTH_TYPE 10 // 会话结束,因为不被允许的认证方式
|
||||
#define TP_SESS_STAT_STARTED 100 // 已经连接成功了,开始记录录像了
|
||||
#define TP_SESS_STAT_ERR_START_INTERNAL 104 // 会话结束,因为内部错误
|
||||
#define TP_SESS_STAT_ERR_START_BAD_PKG 106 // 会话结束,因为收到错误的报文
|
||||
#define TP_SESS_STAT_ERR_START_RESET 107 // 会话结束,因为teleport核心服务重置了
|
||||
#define TP_SESS_STAT_ERR_START_IO 108 // 会话结束,因为网络中断
|
||||
|
||||
|
||||
//=======================================================
|
||||
// 授权标记
|
||||
//=======================================================
|
||||
#define TP_FLAG_ALL 0xFFFFFFFF
|
||||
// 会话记录相关
|
||||
#define TP_FLAG_RECORD_REPLAY 0x00000001 // 允许记录历史(录像回放)
|
||||
#define TP_FLAG_RECORD_REAL_TIME 0x00000002 // 允许实时监控
|
||||
// RDP相关
|
||||
#define TP_FLAG_RDP_DESKTOP 0x00000001 // 允许远程桌面
|
||||
#define TP_FLAG_RDP_CLIPBOARD 0x00000002 // 允许剪贴板
|
||||
#define TP_FLAG_RDP_DISK 0x00000004 // 允许磁盘映射
|
||||
#define TP_FLAG_RDP_APP 0x00000008 // 允许远程APP(尚未实现)
|
||||
#define TP_FLAG_RDP_CONSOLE 0x00001000 //允许连接到管理员会话(RDP的console选项)
|
||||
// SSH相关
|
||||
#define TP_FLAG_SSH_SHELL 0x00000001 // 允许SHELL
|
||||
#define TP_FLAG_SSH_SFTP 0x00000002 // 允许SFTP
|
||||
#define TP_FLAG_SSH_X11 0x00000004 // 允许X11转发(尚未实现)
|
||||
#define TP_FLAG_SSH_EXEC 0x00000008 // 允许exec执行远程命令(尚未实现)
|
||||
#define TP_FLAG_SSH_TUNNEL 0x00000010 // allow ssh tunnel. (not impl.)
|
||||
|
||||
|
||||
//=======================================================
|
||||
// 错误值
|
||||
//=======================================================
|
||||
#define TPE_OK 0 // 成功
|
||||
//-------------------------------------------------------
|
||||
// 通用错误值
|
||||
//-------------------------------------------------------
|
||||
#define TPE_NEED_MORE_DATA 1 // 需要更多数据(不一定是错误)
|
||||
#define TPE_NEED_LOGIN 2 // 需要登录
|
||||
#define TPE_PRIVILEGE 3 // 没有操作权限
|
||||
#define TPE_NOT_IMPLEMENT 7 // 功能尚未实现
|
||||
#define TPE_EXISTS 8 // 目标已经存在
|
||||
#define TPE_NOT_EXISTS 9 // 目标不存在
|
||||
|
||||
// 100~299是通用错误值
|
||||
|
||||
#define TPE_FAILED 100 // 内部错误
|
||||
#define TPE_NETWORK 101 // 网络错误
|
||||
#define TPE_DATABASE 102 // 数据库操作失败
|
||||
|
||||
// HTTP请求相关错误
|
||||
#define TPE_HTTP_METHOD 120 // 无效的请求方法(不是GET/POST等),或者错误的请求方法(例如需要POST,却使用GET方式请求)
|
||||
#define TPE_HTTP_URL_ENCODE 121 // URL编码错误(无法解码)
|
||||
//#define TPE_HTTP_URI 122 // 无效的URI
|
||||
|
||||
#define TPE_UNKNOWN_CMD 124 // 未知的命令
|
||||
#define TPE_JSON_FORMAT 125 // 错误的JSON格式(需要JSON格式数据,但是却无法按JSON格式解码)
|
||||
#define TPE_PARAM 126 // 参数错误
|
||||
#define TPE_DATA 127 // 数据错误
|
||||
|
||||
// #define TPE_OPENFILE_ERROR 0x1007 // 无法打开文件
|
||||
// #define TPE_GETTEMPPATH_ERROR 0x1007
|
||||
#define TPE_OPENFILE 300
|
||||
|
||||
|
||||
//-------------------------------------------------------
|
||||
// WEB服务专用错误值
|
||||
//-------------------------------------------------------
|
||||
#define TPE_CAPTCHA_EXPIRED 10000 // 验证码已过期
|
||||
#define TPE_CAPTCHA_MISMATCH 10001 // 验证码错误
|
||||
#define TPE_OATH_MISMATCH 10002 // 身份验证器动态验证码错误
|
||||
#define TPE_SYS_MAINTENANCE 10003 // 系统维护中
|
||||
|
||||
#define TPE_USER_LOCKED 10100 // 用户已经被锁定(连续多次错误密码)
|
||||
#define TPE_USER_DISABLED 10101 // 用户已经被禁用
|
||||
#define TPE_USER_AUTH 10102 // 身份验证失败
|
||||
|
||||
//-------------------------------------------------------
|
||||
// 助手程序专用错误值
|
||||
//-------------------------------------------------------
|
||||
#define TPE_NO_ASSIST 100000 // 未能检测到助手程序
|
||||
#define TPE_OLD_ASSIST 100001 // 助手程序版本太低
|
||||
#define TPE_START_CLIENT 100002 // 无法启动客户端程序(无法创建进程)
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------
|
||||
// 核心服务专用错误值
|
||||
//-------------------------------------------------------
|
||||
#define TPE_NO_CORE_SERVER 200000 // 未能检测到核心服务
|
||||
|
||||
|
||||
|
||||
#endif // __TELEPORT_CONST_H__
|
||||
#ifndef __TELEPORT_CONST_H__
|
||||
#define __TELEPORT_CONST_H__
|
||||
|
||||
// 注意同步更新三个不同语言的const文件
|
||||
|
||||
// 本文件设定teleport各个模块之间通讯时的错误值(JSON数据),包括:
|
||||
// - WEB界面与助手
|
||||
// - WEB界面与WEB后台
|
||||
// - WEB后台与CORE核心服务
|
||||
|
||||
//=======================================================
|
||||
// Urlprotocol相关
|
||||
//=======================================================
|
||||
#define TP_URLPROTO_APP_NAME "teleport"
|
||||
|
||||
//=======================================================
|
||||
// 远程连接认证方式
|
||||
//=======================================================
|
||||
#define TP_AUTH_TYPE_NONE 0
|
||||
#define TP_AUTH_TYPE_PASSWORD 1
|
||||
#define TP_AUTH_TYPE_PRIVATE_KEY 2
|
||||
|
||||
//=======================================================
|
||||
// 远程连接协议
|
||||
//=======================================================
|
||||
#define TP_PROTOCOL_TYPE_RDP 1
|
||||
#define TP_PROTOCOL_TYPE_SSH 2
|
||||
#define TP_PROTOCOL_TYPE_TELNET 3
|
||||
|
||||
//=======================================================
|
||||
// 远程连接子协议
|
||||
//=======================================================
|
||||
#define TP_PROTOCOL_TYPE_RDP_DESKTOP 100
|
||||
#define TP_PROTOCOL_TYPE_SSH_SHELL 200
|
||||
#define TP_PROTOCOL_TYPE_SSH_SFTP 201
|
||||
#define TP_PROTOCOL_TYPE_TELNET_SHELL 300
|
||||
|
||||
|
||||
//=======================================================
|
||||
// 远程主机操作系统
|
||||
//=======================================================
|
||||
#define TP_OS_TYPE_WINDOWS 1
|
||||
#define TP_OS_TYPE_LINUX 2
|
||||
|
||||
//=======================================================
|
||||
// 远程连接会话状态
|
||||
//=======================================================
|
||||
#define TP_SESS_STAT_RUNNING 0 // 会话开始了,正在连接
|
||||
#define TP_SESS_STAT_END 9999 // 会话成功结束
|
||||
#define TP_SESS_STAT_ERR_AUTH_DENIED 1 // 会话结束,因为认证失败
|
||||
#define TP_SESS_STAT_ERR_CONNECT 2 // 会话结束,因为无法连接到远程主机
|
||||
#define TP_SESS_STAT_ERR_BAD_SSH_KEY 3 // 会话结束,因为无法识别SSH私钥
|
||||
#define TP_SESS_STAT_ERR_INTERNAL 4 // 会话结束,因为内部错误
|
||||
#define TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL 5 // 会话结束,因为协议不支持(RDP)
|
||||
#define TP_SESS_STAT_ERR_BAD_PKG 6 // 会话结束,因为收到错误的报文
|
||||
#define TP_SESS_STAT_ERR_RESET 7 // 会话结束,因为teleport核心服务重置了
|
||||
#define TP_SESS_STAT_ERR_IO 8 // 会话结束,因为网络中断
|
||||
#define TP_SESS_STAT_ERR_SESSION 9 // 会话结束,因为无效的会话ID
|
||||
#define TP_SESS_STAT_ERR_AUTH_TYPE 10 // 会话结束,因为不被允许的认证方式
|
||||
#define TP_SESS_STAT_STARTED 100 // 已经连接成功了,开始记录录像了
|
||||
#define TP_SESS_STAT_ERR_START_INTERNAL 104 // 会话结束,因为内部错误
|
||||
#define TP_SESS_STAT_ERR_START_BAD_PKG 106 // 会话结束,因为收到错误的报文
|
||||
#define TP_SESS_STAT_ERR_START_RESET 107 // 会话结束,因为teleport核心服务重置了
|
||||
#define TP_SESS_STAT_ERR_START_IO 108 // 会话结束,因为网络中断
|
||||
|
||||
|
||||
//=======================================================
|
||||
// 授权标记
|
||||
//=======================================================
|
||||
#define TP_FLAG_ALL 0xFFFFFFFF
|
||||
// 会话记录相关
|
||||
#define TP_FLAG_RECORD_REPLAY 0x00000001 // 允许记录历史(录像回放)
|
||||
#define TP_FLAG_RECORD_REAL_TIME 0x00000002 // 允许实时监控
|
||||
// RDP相关
|
||||
#define TP_FLAG_RDP_DESKTOP 0x00000001 // 允许远程桌面
|
||||
#define TP_FLAG_RDP_CLIPBOARD 0x00000002 // 允许剪贴板
|
||||
#define TP_FLAG_RDP_DISK 0x00000004 // 允许磁盘映射
|
||||
#define TP_FLAG_RDP_APP 0x00000008 // 允许远程APP(尚未实现)
|
||||
#define TP_FLAG_RDP_CONSOLE 0x00001000 //允许连接到管理员会话(RDP的console选项)
|
||||
// SSH相关
|
||||
#define TP_FLAG_SSH_SHELL 0x00000001 // 允许SHELL
|
||||
#define TP_FLAG_SSH_SFTP 0x00000002 // 允许SFTP
|
||||
#define TP_FLAG_SSH_X11 0x00000004 // 允许X11转发(尚未实现)
|
||||
#define TP_FLAG_SSH_EXEC 0x00000008 // 允许exec执行远程命令(尚未实现)
|
||||
#define TP_FLAG_SSH_TUNNEL 0x00000010 // allow ssh tunnel. (not impl.)
|
||||
|
||||
|
||||
//=======================================================
|
||||
// 错误值
|
||||
//=======================================================
|
||||
#define TPE_OK 0 // 成功
|
||||
//-------------------------------------------------------
|
||||
// 通用错误值
|
||||
//-------------------------------------------------------
|
||||
#define TPE_NEED_MORE_DATA 1 // 需要更多数据(不一定是错误)
|
||||
#define TPE_NEED_LOGIN 2 // 需要登录
|
||||
#define TPE_PRIVILEGE 3 // 没有操作权限
|
||||
#define TPE_NOT_IMPLEMENT 7 // 功能尚未实现
|
||||
#define TPE_EXISTS 8 // 目标已经存在
|
||||
#define TPE_NOT_EXISTS 9 // 目标不存在
|
||||
|
||||
// 100~299是通用错误值
|
||||
|
||||
#define TPE_FAILED 100 // 内部错误
|
||||
#define TPE_NETWORK 101 // 网络错误
|
||||
#define TPE_DATABASE 102 // 数据库操作失败
|
||||
|
||||
// HTTP请求相关错误
|
||||
#define TPE_HTTP_METHOD 120 // 无效的请求方法(不是GET/POST等),或者错误的请求方法(例如需要POST,却使用GET方式请求)
|
||||
#define TPE_HTTP_URL_ENCODE 121 // URL编码错误(无法解码)
|
||||
//#define TPE_HTTP_URI 122 // 无效的URI
|
||||
|
||||
#define TPE_UNKNOWN_CMD 124 // 未知的命令
|
||||
#define TPE_JSON_FORMAT 125 // 错误的JSON格式(需要JSON格式数据,但是却无法按JSON格式解码)
|
||||
#define TPE_PARAM 126 // 参数错误
|
||||
#define TPE_DATA 127 // 数据错误
|
||||
|
||||
// #define TPE_OPENFILE_ERROR 0x1007 // 无法打开文件
|
||||
// #define TPE_GETTEMPPATH_ERROR 0x1007
|
||||
#define TPE_OPENFILE 300
|
||||
|
||||
|
||||
//-------------------------------------------------------
|
||||
// WEB服务专用错误值
|
||||
//-------------------------------------------------------
|
||||
#define TPE_CAPTCHA_EXPIRED 10000 // 验证码已过期
|
||||
#define TPE_CAPTCHA_MISMATCH 10001 // 验证码错误
|
||||
#define TPE_OATH_MISMATCH 10002 // 身份验证器动态验证码错误
|
||||
#define TPE_SYS_MAINTENANCE 10003 // 系统维护中
|
||||
|
||||
#define TPE_USER_LOCKED 10100 // 用户已经被锁定(连续多次错误密码)
|
||||
#define TPE_USER_DISABLED 10101 // 用户已经被禁用
|
||||
#define TPE_USER_AUTH 10102 // 身份验证失败
|
||||
|
||||
//-------------------------------------------------------
|
||||
// 助手程序专用错误值
|
||||
//-------------------------------------------------------
|
||||
#define TPE_NO_ASSIST 100000 // 未能检测到助手程序
|
||||
#define TPE_OLD_ASSIST 100001 // 助手程序版本太低
|
||||
#define TPE_START_CLIENT 100002 // 无法启动客户端程序(无法创建进程)
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------
|
||||
// 核心服务专用错误值
|
||||
//-------------------------------------------------------
|
||||
#define TPE_NO_CORE_SERVER 200000 // 未能检测到核心服务
|
||||
|
||||
|
||||
|
||||
#endif // __TELEPORT_CONST_H__
|
||||
|
|
|
@ -22,6 +22,8 @@ wget = C:\Program Files (x86)\wget\wget.exe
|
|||
# if not set msbuild path, default to get it by register.
|
||||
#msbuild = C:\Program Files (x86)\MSBuild\14.0\bin\MSBuild.exe
|
||||
|
||||
# need qt to build tp-player.
|
||||
qt = C:\Qt\Qt5.12.0\5.12.0\msvc2017
|
||||
|
||||
# ============================================
|
||||
# for Linux and macOS
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
[external_ver]
|
||||
openssl = 1.0.2p,1000210f
|
||||
libuv = 1.28.0
|
||||
mbedtls = 2.12.0
|
||||
libssh = 0.9.0
|
||||
jsoncpp = 0.10.6
|
||||
mongoose = 6.12
|
||||
|
||||
; https://github.com/openssl/openssl/releases
|
||||
; http://slproweb.com/download/Win32OpenSSL-1_1_1d.exe
|
||||
openssl = 1.1.1d,1010104f
|
||||
; https://github.com/libuv/libuv/releases
|
||||
libuv = 1.33.1
|
||||
; https://github.com/ARMmbed/mbedtls/releases
|
||||
mbedtls = 2.16.3
|
||||
; https://github.com/open-source-parsers/jsoncpp/releases
|
||||
jsoncpp = 1.9.2
|
||||
; https://github.com/cesanta/mongoose/releases
|
||||
mongoose = 6.16
|
||||
; https://www.zlib.net/zlib1211.zip
|
||||
zlib = 1.2.11,1211
|
||||
; https://git.libssh.org/projects/libssh.git/
|
||||
libssh = 0.9.2
|
||||
|
|
|
@ -1,48 +1,48 @@
|
|||
#include "base_env.h"
|
||||
|
||||
TppEnvBase::TppEnvBase()
|
||||
{}
|
||||
|
||||
TppEnvBase::~TppEnvBase()
|
||||
{}
|
||||
|
||||
bool TppEnvBase::init(TPP_INIT_ARGS* args)
|
||||
{
|
||||
if (NULL == args)
|
||||
{
|
||||
EXLOGE("invalid init args(1).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
EXLOG_USE_LOGGER(args->logger);
|
||||
|
||||
exec_path = args->exec_path;
|
||||
etc_path = args->etc_path;
|
||||
replay_path = args->replay_path;
|
||||
|
||||
get_connect_info = args->func_get_connect_info;
|
||||
free_connect_info = args->func_free_connect_info;
|
||||
session_begin = args->func_session_begin;
|
||||
session_update = args->func_session_update;
|
||||
session_end = args->func_session_end;
|
||||
|
||||
if (NULL == get_connect_info || NULL == free_connect_info || NULL == session_begin || NULL == session_update || NULL == session_end)
|
||||
{
|
||||
EXLOGE("invalid init args(2).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NULL == args->cfg)
|
||||
{
|
||||
EXLOGE("invalid init args(3).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_on_init(args))
|
||||
{
|
||||
EXLOGE("invalid init args(4).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#include "base_env.h"
|
||||
|
||||
TppEnvBase::TppEnvBase()
|
||||
{}
|
||||
|
||||
TppEnvBase::~TppEnvBase()
|
||||
{}
|
||||
|
||||
bool TppEnvBase::init(TPP_INIT_ARGS* args)
|
||||
{
|
||||
if (NULL == args)
|
||||
{
|
||||
EXLOGE("invalid init args(1).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
EXLOG_USE_LOGGER(args->logger);
|
||||
|
||||
exec_path = args->exec_path;
|
||||
etc_path = args->etc_path;
|
||||
replay_path = args->replay_path;
|
||||
|
||||
get_connect_info = args->func_get_connect_info;
|
||||
free_connect_info = args->func_free_connect_info;
|
||||
session_begin = args->func_session_begin;
|
||||
session_update = args->func_session_update;
|
||||
session_end = args->func_session_end;
|
||||
|
||||
if (NULL == get_connect_info || NULL == free_connect_info || NULL == session_begin || NULL == session_update || NULL == session_end)
|
||||
{
|
||||
EXLOGE("invalid init args(2).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NULL == args->cfg)
|
||||
{
|
||||
EXLOGE("invalid init args(3).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_on_init(args))
|
||||
{
|
||||
EXLOGE("invalid init args(4).\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
#ifndef __TS_BASE_ENV_H__
|
||||
#define __TS_BASE_ENV_H__
|
||||
|
||||
#include "protocol_interface.h"
|
||||
|
||||
class TppEnvBase
|
||||
{
|
||||
public:
|
||||
TppEnvBase();
|
||||
virtual ~TppEnvBase();
|
||||
|
||||
bool init(TPP_INIT_ARGS* args);
|
||||
|
||||
public:
|
||||
ex_wstr exec_path;
|
||||
ex_wstr etc_path; // 配置文件、SSH服务器的私钥文件的存放路径
|
||||
ex_wstr replay_path;
|
||||
|
||||
TPP_GET_CONNNECT_INFO_FUNC get_connect_info;
|
||||
TPP_FREE_CONNECT_INFO_FUNC free_connect_info;
|
||||
TPP_SESSION_BEGIN_FUNC session_begin;
|
||||
TPP_SESSION_UPDATE_FUNC session_update;
|
||||
TPP_SESSION_END_FUNC session_end;
|
||||
|
||||
protected:
|
||||
virtual bool _on_init(TPP_INIT_ARGS* args) = 0;
|
||||
};
|
||||
|
||||
#endif // __TS_BASE_ENV_H__
|
||||
#ifndef __TS_BASE_ENV_H__
|
||||
#define __TS_BASE_ENV_H__
|
||||
|
||||
#include "protocol_interface.h"
|
||||
|
||||
class TppEnvBase
|
||||
{
|
||||
public:
|
||||
TppEnvBase();
|
||||
virtual ~TppEnvBase();
|
||||
|
||||
bool init(TPP_INIT_ARGS* args);
|
||||
|
||||
public:
|
||||
ex_wstr exec_path;
|
||||
ex_wstr etc_path; // 配置文件、SSH服务器的私钥文件的存放路径
|
||||
ex_wstr replay_path;
|
||||
|
||||
TPP_GET_CONNNECT_INFO_FUNC get_connect_info;
|
||||
TPP_FREE_CONNECT_INFO_FUNC free_connect_info;
|
||||
TPP_SESSION_BEGIN_FUNC session_begin;
|
||||
TPP_SESSION_UPDATE_FUNC session_update;
|
||||
TPP_SESSION_END_FUNC session_end;
|
||||
|
||||
protected:
|
||||
virtual bool _on_init(TPP_INIT_ARGS* args) = 0;
|
||||
};
|
||||
|
||||
#endif // __TS_BASE_ENV_H__
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include <memory>
|
||||
#include <memory>
|
||||
|
||||
#include "base_record.h"
|
||||
|
||||
|
|
|
@ -1,101 +1,109 @@
|
|||
#ifndef __TS_BASE_RECORD_H__
|
||||
#define __TS_BASE_RECORD_H__
|
||||
|
||||
#include "base_env.h"
|
||||
#include "ts_membuf.h"
|
||||
#include "protocol_interface.h"
|
||||
|
||||
#include <ex.h>
|
||||
|
||||
#define MAX_SIZE_PER_FILE 4194304 // 4M = 1024*1024*4
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
/*
|
||||
* 录像
|
||||
*
|
||||
* 一个录像分为两个文件,一个信息文件,一个数据文件。
|
||||
* 服务内部缓存最大4M,或者5秒,就将数据写入数据文件中,并同时更新信息文件。
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// 录像文件头(随着录像数据写入,会改变的部分)
|
||||
typedef struct TS_RECORD_HEADER_INFO
|
||||
{
|
||||
ex_u32 magic; // "TPPR" 标志 TelePort Protocol Record
|
||||
ex_u16 ver; // 录像文件版本,目前为3
|
||||
ex_u32 packages; // 总包数
|
||||
ex_u32 time_ms; // 总耗时(毫秒)
|
||||
//ex_u32 file_size; // 数据文件大小
|
||||
}TS_RECORD_HEADER_INFO;
|
||||
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
|
||||
|
||||
// 录像文件头(固定不变部分)
|
||||
typedef struct TS_RECORD_HEADER_BASIC
|
||||
{
|
||||
ex_u16 protocol_type; // 协议:1=RDP, 2=SSH, 3=Telnet
|
||||
ex_u16 protocol_sub_type; // 子协议:100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
|
||||
ex_u64 timestamp; // 本次录像的起始时间(UTC时间戳)
|
||||
ex_u16 width; // 初始屏幕尺寸:宽
|
||||
ex_u16 height; // 初始屏幕尺寸:高
|
||||
char user_username[64]; // teleport账号
|
||||
char acc_username[64]; // 远程主机用户名
|
||||
|
||||
char host_ip[40]; // 远程主机IP
|
||||
char conn_ip[40]; // 远程主机IP
|
||||
ex_u16 conn_port; // 远程主机端口
|
||||
|
||||
char client_ip[40]; // 客户端IP
|
||||
|
||||
// RDP专有
|
||||
ex_u8 rdp_security; // 0 = RDP, 1 = TLS
|
||||
|
||||
ex_u8 _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - 1 - ts_record_header_info_size];
|
||||
}TS_RECORD_HEADER_BASIC;
|
||||
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
|
||||
|
||||
typedef struct TS_RECORD_HEADER
|
||||
{
|
||||
TS_RECORD_HEADER_INFO info;
|
||||
TS_RECORD_HEADER_BASIC basic;
|
||||
}TS_RECORD_HEADER;
|
||||
|
||||
// header部分(header-info + header-basic) = 512B
|
||||
#define ts_record_header_size sizeof(TS_RECORD_HEADER)
|
||||
|
||||
|
||||
// 一个数据包的头
|
||||
typedef struct TS_RECORD_PKG
|
||||
{
|
||||
ex_u8 type; // 包的数据类型
|
||||
ex_u32 size; // 这个包的总大小(不含包头)
|
||||
ex_u32 time_ms; // 这个包距起始时间的时间差(毫秒,意味着一个连接不能持续超过49天)
|
||||
ex_u8 _reserve[3]; // 保留
|
||||
}TS_RECORD_PKG;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
class TppRecBase
|
||||
{
|
||||
public:
|
||||
TppRecBase();
|
||||
virtual ~TppRecBase();
|
||||
|
||||
bool begin(const wchar_t* base_path, const wchar_t* base_fname, int record_id, const TPP_CONNECT_INFO* info);
|
||||
bool end();
|
||||
|
||||
protected:
|
||||
virtual bool _on_begin(const TPP_CONNECT_INFO* info) = 0;
|
||||
virtual bool _on_end() = 0;
|
||||
|
||||
protected:
|
||||
ex_wstr m_base_path; // 录像文件基础路径,例如 /usr/local/teleport/data/replay/ssh/123,数字编号是内部附加的,作为本次会话录像文件的目录名称
|
||||
ex_wstr m_base_fname; // 录像文件的文件名,不含扩展名部分,内部会以此为基础合成文件全名,并将录像文件存放在 m_base_path 指向的目录中
|
||||
|
||||
ex_u64 m_start_time;
|
||||
|
||||
MemBuffer m_cache;
|
||||
};
|
||||
|
||||
#endif // __TS_BASE_RECORD_H__
|
||||
#ifndef __TS_BASE_RECORD_H__
|
||||
#define __TS_BASE_RECORD_H__
|
||||
|
||||
#include "base_env.h"
|
||||
#include "ts_membuf.h"
|
||||
#include "protocol_interface.h"
|
||||
|
||||
#include <ex.h>
|
||||
|
||||
#define MAX_CACHE_SIZE 1048576 // 1M = 1024*1024*1
|
||||
#define MAX_SIZE_PER_FILE 4194304 // 4M = 1024*1024*4
|
||||
// for test.
|
||||
// #define MAX_CACHE_SIZE 524288 // 512KB = 512*1024
|
||||
// #define MAX_SIZE_PER_FILE 1048576 // 1M = 1024*1024*1
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
/*
|
||||
* 录像
|
||||
*
|
||||
* 一个录像分为多个文件:
|
||||
* *.tpr,录像信息文件,一个,固定大小(512字节)
|
||||
* *.tpd,数据文件,n个,例如 tp-rdp-1.tpd,tp-rdp-2.tpd等等,每个数据文件约4MB
|
||||
* *.tpk,关键帧信息文件,一个,仅RDP录像,记录各个关键帧数据所在的数据文件序号、偏移、时间点等信息。
|
||||
* *-cmd.txt,ssh命令记录文件,仅SSH。
|
||||
* 服务内部缓存最大4M,或者5秒,就将数据写入数据文件中,并同时更新信息文件。
|
||||
*
|
||||
*/
|
||||
|
||||
#define TS_TPPR_TYPE_UNKNOWN 0x0000
|
||||
#define TS_TPPR_TYPE_SSH 0x0001
|
||||
#define TS_TPPR_TYPE_RDP 0x0101
|
||||
|
||||
// 录像文件头(随着录像数据写入,会改变的部分)
|
||||
typedef struct TS_RECORD_HEADER_INFO {
|
||||
ex_u32 magic; // "TPPR" 标志 TelePort Protocol Record
|
||||
ex_u16 ver; // 录像文件版本,v3.5.0开始为4
|
||||
ex_u16 type; // 录像内容,SSH or RDP
|
||||
// ex_u32 packages; // 总包数
|
||||
ex_u32 time_ms; // 总耗时(毫秒)
|
||||
ex_u32 dat_file_count; // 数据文件数量
|
||||
ex_u8 _reserve[64-4-2-2-4-4];
|
||||
}TS_RECORD_HEADER_INFO;
|
||||
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
|
||||
|
||||
// 录像文件头(固定不变部分)
|
||||
typedef struct TS_RECORD_HEADER_BASIC {
|
||||
ex_u16 protocol_type; // 协议:1=RDP, 2=SSH, 3=Telnet
|
||||
ex_u16 protocol_sub_type; // 子协议:100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
|
||||
ex_u64 timestamp; // 本次录像的起始时间(UTC时间戳)
|
||||
ex_u16 width; // 初始屏幕尺寸:宽
|
||||
ex_u16 height; // 初始屏幕尺寸:高
|
||||
char user_username[64]; // teleport账号
|
||||
char acc_username[64]; // 远程主机用户名
|
||||
|
||||
char host_ip[40]; // 远程主机IP
|
||||
char conn_ip[40]; // 远程主机IP
|
||||
ex_u16 conn_port; // 远程主机端口
|
||||
|
||||
char client_ip[40]; // 客户端IP
|
||||
|
||||
// // RDP专有 - v3.5.0废弃并移除
|
||||
// ex_u8 rdp_security; // 0 = RDP, 1 = TLS
|
||||
|
||||
ex_u8 _reserve[512 - ts_record_header_info_size - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40];
|
||||
}TS_RECORD_HEADER_BASIC;
|
||||
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
|
||||
|
||||
typedef struct TS_RECORD_HEADER {
|
||||
TS_RECORD_HEADER_INFO info;
|
||||
TS_RECORD_HEADER_BASIC basic;
|
||||
}TS_RECORD_HEADER;
|
||||
|
||||
// header部分(header-info + header-basic) = 512B
|
||||
#define ts_record_header_size sizeof(TS_RECORD_HEADER)
|
||||
|
||||
// 一个数据包的头
|
||||
typedef struct TS_RECORD_PKG {
|
||||
ex_u8 type; // 包的数据类型
|
||||
ex_u8 _reserve[3]; // 保留
|
||||
ex_u32 size; // 这个包的总大小(不含包头)
|
||||
ex_u32 time_ms; // 这个包距起始时间的时间差(毫秒,意味着一个连接不能持续超过49天)
|
||||
//ex_u32 index; // 这个包的序号(最后一个包的序号与TS_RECORD_HEADER_INFO::packages数量匹配)
|
||||
}TS_RECORD_PKG;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
class TppRecBase {
|
||||
public:
|
||||
TppRecBase();
|
||||
virtual ~TppRecBase();
|
||||
|
||||
bool begin(const wchar_t* base_path, const wchar_t* base_fname, int record_id, const TPP_CONNECT_INFO* info);
|
||||
bool end();
|
||||
|
||||
protected:
|
||||
virtual bool _on_begin(const TPP_CONNECT_INFO* info) = 0;
|
||||
virtual bool _on_end() = 0;
|
||||
|
||||
protected:
|
||||
ex_wstr m_base_path; // 录像文件基础路径,例如 /usr/local/teleport/data/replay/ssh/123,数字编号是内部附加的,作为本次会话录像文件的目录名称
|
||||
ex_wstr m_base_fname; // 录像文件的文件名,不含扩展名部分,内部会以此为基础合成文件全名,并将录像文件存放在 m_base_path 指向的目录中
|
||||
|
||||
ex_u64 m_start_time;
|
||||
|
||||
MemBuffer m_cache;
|
||||
};
|
||||
|
||||
#endif // __TS_BASE_RECORD_H__
|
||||
|
|