contest: separate out generic contest logic from ui code

In a way, this demotes the UI from the central point of the contest binary
to being just one of potentially many ways to input entries into the system.

Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
5 files changed, 366 insertions(+), 269 deletions(-)

M hlog/CMakeLists.txt
M hlog/contest.c
M hlog/contest/contest.h
A => hlog/contest/core.c
M hlog/contest/ui.c
M hlog/CMakeLists.txt +1 -0
@@ 53,6 53,7 @@ add_executable(hlog-contest
 	common/xcurses.c
 	common/xform.c
 	contest/callout.c
+	contest/core.c
 	contest/location.c
 	contest/lualib.c
 	contest/msg.c

          
M hlog/contest.c +3 -3
@@ 378,7 378,7 @@ int main(int argc, char **argv)
 		if (ret)
 			goto err_msg;
 
-		ret = contest_init(&contest_params);
+		ret = contest_core_init(&contest_params);
 		if (ret)
 			goto err_listen;
 	}

          
@@ 390,7 390,7 @@ int main(int argc, char **argv)
 	}
 
 	if (!export)
-		ret = contest_run(lua, wfields, nwfields);
+		ret = contest_ui_run(lua, &contest_params, wfields, nwfields);
 	else
 		ret = do_export(lua, &contest_params, export);
 

          
@@ 398,7 398,7 @@ int main(int argc, char **argv)
 
 err_free:
 	if (!export)
-		contest_cleanup();
+		contest_core_cleanup();
 
 err_listen:
 	if (!export)

          
M hlog/contest/contest.h +9 -4
@@ 41,10 41,15 @@ struct contest_params {
 	struct qso *template;
 };
 
-extern int contest_init(struct contest_params *contest_params);
-extern int contest_run(struct xlua_state *lua, struct xfield_def *wfields,
-		       size_t nwfields);
-extern void contest_cleanup(void);
+extern int contest_core_init(struct contest_params *contest_params);
+extern void contest_core_cleanup(void);
+extern uint32_t contest_core_sync_location(struct qso *qso, int *mode_r,
+					   uint32_t prev_gen);
+extern uint32_t contest_core_sync_rig(struct qso *qso, uint32_t prev_gen);
+
+extern int contest_ui_run(struct xlua_state *lua,
+			  struct contest_params *contest_params,
+			  struct xfield_def *wfields, size_t nwfields);
 
 /*
  * interactive forms

          
A => hlog/contest/core.c +312 -0
@@ 0,0 1,312 @@ 
+/*
+ * Copyright (c) 2020-2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <jeffpc/timer.h>
+#include <jeffpc/sock.h>
+
+#include <hlog/qso.h>
+#include <hlog/maidenhead.h>
+
+#include <hlog-rpc/announce.h>
+#include <hlog-rpc/announce-pub.h>
+#include <hlog-rpc/announce-sub.h>
+#include <hlog-rpc/rig-mode.h>
+
+#include "../common/config.h"
+
+#include "contest.h"
+
+static const struct contest_params *contest_params;
+
+static struct location {
+	uint32_t gen;
+	int mode;
+	char grid[MAIDENHEAD_MAX_STRING_LENGTH];
+} location;
+static struct lock location_lock;
+static LOCK_CLASS(location_lc);
+
+static struct rig_info {
+	uint64_t tx_freq;
+	uint64_t rx_freq;
+	uint64_t power;
+	enum hrig_mode tx_mode;
+	enum hrig_mode rx_mode;
+	uint32_t gen;
+} rig_info;
+static struct lock rig_lock;
+static LOCK_CLASS(rig_lc);
+
+static struct periodic info_periodic;
+
+/*
+ * location
+ */
+static void handle_location_notification(enum announce_loc_msg_type type,
+					 const struct instance_info *info)
+{
+	const int mode = info->loc.mode;
+	const char *grid = info->loc.grid;
+
+	if (!info->self)
+		return; /* ignore anything other than our own location */
+
+	MXLOCK(&location_lock);
+	if ((location.mode != mode) ||
+	    (mode && strcmp(location.grid, grid))) {
+		struct qso_side *tx;
+
+		/* stash raw values for future comparison */
+		if (mode)
+			strcpy_safe(location.grid, grid, sizeof(location.grid));
+		else
+			location.grid[0] = '\0';
+		location.mode = mode;
+		location.gen++;
+
+		/* update template */
+		tx = qso_get_tx_side(contest_params->template);
+
+		if (mode)
+			qso_set_grid(tx, location.grid);
+		else
+			qso_clear_grid(tx);
+	}
+	MXUNLOCK(&location_lock);
+}
+
+uint32_t contest_core_sync_location(struct qso *qso, int *mode_r,
+				    uint32_t prev_gen)
+{
+	MXLOCK(&location_lock);
+	if (location.gen != prev_gen) {
+		struct qso_side *t_tx;
+		struct qso_side *q_tx;
+
+		t_tx = qso_get_tx_side(contest_params->template);
+		q_tx = qso_get_tx_side(qso);
+
+		qso_set_grid_val(q_tx, qso_get_grid_val(t_tx));
+		*mode_r = location.mode;
+
+		prev_gen = location.gen;
+	}
+	MXUNLOCK(&location_lock);
+
+	return prev_gen;
+}
+
+/*
+ * rig
+ */
+static void __set_rig_info(struct qso_side *side, uint64_t v,
+			   struct val *(*get)(const struct qso_side *),
+			   int (*set)(struct qso_side *, struct val *),
+			   void (*clr)(struct qso_side *))
+{
+	struct val *cur;
+
+	if (!v)
+		return;
+
+	cur = get(side);
+	if (!cur || (cur->type != VT_INT) || (cur->i != v)) {
+		if (set(side, VAL_ALLOC_INT(v)))
+			clr(side);
+	}
+
+	val_putref(cur);
+}
+
+#define __set_rig_info_freq(side, freq) \
+	__set_rig_info((side), (freq), \
+		       qso_get_freq, qso_set_freq_val, qso_clear_freq)
+#define __set_rig_info_power(side, power) \
+	__set_rig_info((side), (power), \
+		       qso_get_power, qso_set_power_val, qso_clear_power)
+
+static void __set_rig_info_mode(struct qso_side *side, enum hrig_mode mode)
+{
+	const char *modename;
+
+	modename = hamlib_mode_name(mode);
+	if (!modename)
+		qso_clear_mode(side);
+	else
+		qso_set_mode(side, modename);
+}
+
+static void handle_rig_notification(enum announce_rig_msg_type type,
+				    const struct instance_info *info)
+{
+	const uint64_t tx_freq = info->rig.tx_freq;
+	const uint64_t rx_freq = info->rig.rx_freq;
+	const uint32_t power = info->rig.power;
+	enum hrig_mode tx_mode = info->rig.tx_mode;
+	enum hrig_mode rx_mode = info->rig.rx_mode;
+
+	if (!info->self)
+		return; /* ignore anything other than our own rig */
+
+	MXLOCK(&rig_lock);
+	if ((rig_info.tx_freq != tx_freq) || (rig_info.rx_freq != rx_freq) ||
+	    (rig_info.power != power) ||
+	    (rig_info.tx_mode != tx_mode) || (rig_info.rx_mode != rx_mode)) {
+		struct qso_side *tx, *rx;
+
+		/* stash raw values for future comparison */
+		rig_info.tx_freq = tx_freq;
+		rig_info.rx_freq = rx_freq;
+		rig_info.power = power;
+		rig_info.tx_mode = tx_mode;
+		rig_info.rx_mode = rx_mode;
+		rig_info.gen++;
+
+		/* update template */
+		tx = qso_get_tx_side(contest_params->template);
+		rx = qso_get_rx_side(contest_params->template);
+
+		__set_rig_info_freq(tx, rig_info.tx_freq);
+		__set_rig_info_freq(rx, rig_info.rx_freq);
+		__set_rig_info_power(tx, rig_info.power);
+		__set_rig_info_mode(tx, rig_info.tx_mode);
+		__set_rig_info_mode(rx, rig_info.rx_mode);
+	}
+	MXUNLOCK(&rig_lock);
+}
+
+uint32_t contest_core_sync_rig(struct qso *qso, uint32_t prev_gen)
+{
+	MXLOCK(&rig_lock);
+	if (rig_info.gen != prev_gen) {
+		struct qso_side *t_tx, *t_rx;
+		struct qso_side *q_tx, *q_rx;
+
+		t_tx = qso_get_tx_side(contest_params->template);
+		t_rx = qso_get_rx_side(contest_params->template);
+
+		q_tx = qso_get_tx_side(qso);
+		q_rx = qso_get_rx_side(qso);
+
+		qso_set_freq_val(q_tx, qso_get_freq(t_tx));
+		qso_set_freq_val(q_rx, qso_get_freq(t_rx));
+		qso_set_power_val(q_tx, qso_get_power(t_tx));
+		qso_set_mode_val(q_tx, qso_get_mode_val(t_tx));
+		qso_set_mode_val(q_rx, qso_get_mode_val(t_rx));
+
+		prev_gen = rig_info.gen;
+	}
+	MXUNLOCK(&rig_lock);
+
+	return prev_gen;
+}
+
+/*
+ * periodic info announcements
+ */
+static void info_periodic_cb(struct periodic *periodic, uint64_t now,
+			     void *private)
+{
+	const struct contest_params *params = contest_params;
+	struct qso *template = params->template;
+	struct qso_side *tx;
+
+	tx = qso_get_tx_side(template);
+
+	announce_publish_info(xgethostname(),
+			      str_getref(params->name),
+			      qso_get_operator_call(tx),
+			      qso_get_country(tx),
+			      qso_get_state(tx),
+			      qso_get_county(tx),
+			      qso_get_zone_dxcc(tx),
+			      qso_get_zone_cqz(tx),
+			      qso_get_zone_itu(tx));
+}
+
+int contest_core_init(struct contest_params *params)
+{
+	struct qso *template;
+	int ret;
+
+	contest_params = params;
+
+	MXINIT(&location_lock, &location_lc);
+	MXINIT(&rig_lock, &rig_lc);
+
+	/* something !0 */
+	location.gen = 0x12345678;
+	rig_info.gen = 0x12345678;
+
+	template = qso_alloc();
+	if (!template) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ret = load_config(AT_FDCWD, template, "contest");
+	if (ret)
+		goto err_free;
+
+	params->template = template;
+
+	/*
+	 * Note: the announcement callbacks make use of the template
+	 */
+	ret = announce_connect(net_update_row,
+			       NULL,
+			       handle_location_notification,
+			       handle_rig_notification);
+	if (ret)
+		goto err_free;
+
+	/*
+	 * Note: the periodic timer makes uses of the template!
+	 */
+	periodic_init(&info_periodic, info_periodic_cb, NULL);
+	periodic_set_period_sec(&info_periodic, ANNOUNCE_INFO_PERIOD);
+	periodic_arm(&info_periodic);
+
+	return 0;
+
+err_free:
+	qso_putref(template);
+
+err:
+	MXDESTROY(&rig_lock);
+	MXDESTROY(&location_lock);
+
+	return ret;
+}
+
+void contest_core_cleanup(void)
+{
+	periodic_disarm(&info_periodic);
+
+	/* disconnect from announcements - a little racy, but good enough */
+	announce_connect(NULL, NULL, NULL, NULL);
+
+	qso_putref(contest_params->template);
+
+	MXDESTROY(&rig_lock);
+	MXDESTROY(&location_lock);
+}

          
M hlog/contest/ui.c +41 -262
@@ 21,8 21,6 @@ 
  */
 
 #include <jeffpc/synch.h>
-#include <jeffpc/timer.h>
-#include <jeffpc/sock.h>
 
 #include <hlog/qso.h>
 #include <hlog/maidenhead.h>

          
@@ 32,7 30,6 @@ 
 #include <hlog-rpc/announce.h>
 #include <hlog-rpc/announce-pub.h>
 #include <hlog-rpc/announce-sub.h>
-#include <hlog-rpc/rig-mode.h>
 
 #include "../common/config.h"
 #include "../common/recent.h"

          
@@ 105,27 102,6 @@ static const struct contest_params *cont
 
 static struct qso *contact;
 
-static struct location {
-	uint32_t gen;
-	int mode;
-	char grid[MAIDENHEAD_MAX_STRING_LENGTH];
-} location;
-static struct lock location_lock;
-static LOCK_CLASS(location_lc);
-
-static struct rig_info {
-	uint64_t tx_freq;
-	uint64_t rx_freq;
-	uint64_t power;
-	enum hrig_mode tx_mode;
-	enum hrig_mode rx_mode;
-	uint32_t gen;
-} rig_info;
-static struct lock rig_lock;
-static LOCK_CLASS(rig_lc);
-
-static struct periodic info_periodic;
-
 static WINDOW *header_win; /* location & time */
 static WINDOW *supercheck_win;
 struct recent recent_work_list; /* current contact history */

          
@@ 382,197 358,75 @@ static struct val *get_qso_freq(const st
 	return VAL_DUP_STR(buf);
 }
 
-static void handle_qso_notification(enum announce_qso_msg_type type,
-				    const struct instance_info *info,
-				    const struct xuuid *uuid)
-{
-	const char *action = (type == ANNOUNCE_QSO_NEW) ? "added" : "updated";
-	char uuid_str[XUUID_PRINTABLE_STRING_LENGTH];
-
-	xuuid_unparse(uuid, uuid_str);
-
-	msg_print(MSG_QSO, CE_INFO, "QSO %-7s %s", action, uuid_str);
-}
-
 /*
  * location
  */
-static void handle_location_notification(enum announce_loc_msg_type type,
-					 const struct instance_info *info)
-{
-	const int mode = info->loc.mode;
-	const char *grid = info->loc.grid;
-
-	if (!info->self)
-		return; /* ignore anything other than our own location */
-
-	MXLOCK(&location_lock);
-	if ((location.mode != mode) ||
-	    (mode && strcmp(location.grid, grid))) {
-		/* something changed */
-		if (mode)
-			strcpy_safe(location.grid, grid, sizeof(location.grid));
-		else
-			location.grid[0] = '\0';
-		location.mode = mode;
-		location.gen++;
-	}
-	MXUNLOCK(&location_lock);
-}
-
 static void draw_location(void)
 {
 	static uint32_t prev_gen;
-	struct location info;
-	const char *grid;
+	uint32_t new_gen;
+	struct str *grid;
+	int mode;
 
-	MXLOCK(&location_lock);
-	info = location;
-	MXUNLOCK(&location_lock);
+	new_gen = contest_core_sync_location(contact, &mode, prev_gen);
+	if (new_gen == prev_gen)
+		return;
 
-	if (prev_gen == info.gen)
-		return; /* no change */
+	prev_gen = new_gen;
 
-	grid = info.grid[0] ? info.grid : UNKNOWN_LOCATION_STR;
+	grid = qso_get_grid(qso_get_tx_side(contact));
 
-	mvwprintw(header_win, 0, 10, "%-*s", sizeof(info.grid), grid);
+	mvwprintw(header_win, 0, 10, "%-*s", MAIDENHEAD_MAX_STRING_LENGTH,
+		  mode ?  str_cstr(grid) : UNKNOWN_LOCATION_STR);
 
-	if (info.mode)
+	if (mode)
 		msg_print(MSG_GEO, CE_INFO, "Location changed: %uD fix @ %s",
-			  info.mode, grid);
+			  mode, str_cstr(grid));
 	else
 		msg_print(MSG_GEO, CE_INFO, "Location changed: fix lost");
+
+	str_putref(grid);
 }
 
 /*
  * rig
  */
-static void handle_rig_notification(enum announce_rig_msg_type type,
-				    const struct instance_info *info)
-{
-	const uint64_t tx_freq = info->rig.tx_freq;
-	const uint64_t rx_freq = info->rig.rx_freq;
-	const uint32_t power = info->rig.power;
-	enum hrig_mode tx_mode = info->rig.tx_mode;
-	enum hrig_mode rx_mode = info->rig.rx_mode;
-
-	if (!info->self)
-		return; /* ignore anything other than our own rig */
-
-	MXLOCK(&rig_lock);
-	if ((rig_info.tx_freq != tx_freq) || (rig_info.rx_freq != rx_freq) ||
-	    (rig_info.power != power) ||
-	    (rig_info.tx_mode != tx_mode) || (rig_info.rx_mode != rx_mode)) {
-		/* something changed */
-		rig_info.tx_freq = tx_freq;
-		rig_info.rx_freq = rx_freq;
-		rig_info.power = power;
-		rig_info.tx_mode = tx_mode;
-		rig_info.rx_mode = rx_mode;
-		rig_info.gen++;
-	}
-	MXUNLOCK(&rig_lock);
-}
-
-static void __set_rig_info(struct qso_side *side, uint64_t v,
-			   struct val *(*get)(const struct qso_side *),
-			   int (*set)(struct qso_side *, struct val *),
-			   void (*clr)(struct qso_side *))
-{
-	struct val *cur;
-
-	if (!v)
-		return;
-
-	cur = get(side);
-	if (!cur || (cur->type != VT_INT) || (cur->i != v)) {
-		if (set(side, VAL_ALLOC_INT(v)))
-			clr(side);
-	}
-
-	val_putref(cur);
-}
-
-#define __set_rig_info_freq(side, freq) \
-	__set_rig_info((side), (freq), \
-		       qso_get_freq, qso_set_freq_val, qso_clear_freq)
-#define __set_rig_info_power(side, power) \
-	__set_rig_info((side), (power), \
-		       qso_get_power, qso_set_power_val, qso_clear_power)
-
-static void __set_rig_info_mode(struct qso_side *side, enum hrig_mode mode)
-{
-	const char *modename;
-
-	modename = hamlib_mode_name(mode);
-	if (!modename)
-		qso_clear_mode(side);
-	else
-		qso_set_mode(side, modename);
-}
-
 static void set_rig_info(void)
 {
 	static uint32_t prev_gen;
+	uint32_t new_gen;
 	struct qso_side *tx, *rx;
-	struct rig_info info;
+	struct str *tx_mode;
+	struct str *rx_mode;
+	char tx_freq[30];
+	char rx_freq[30];
+	char power[30];
 
-	MXLOCK(&rig_lock);
-	info = rig_info;
-	MXUNLOCK(&rig_lock);
+	new_gen = contest_core_sync_rig(contact, prev_gen);
+	if (new_gen == prev_gen)
+		return;
 
-	if (prev_gen == info.gen)
-		return; /* no change */
+	prev_gen = new_gen;
 
 	tx = qso_get_tx_side(contact);
 	rx = qso_get_rx_side(contact);
 
-	__set_rig_info_freq(tx, info.tx_freq);
-	__set_rig_info_freq(rx, info.rx_freq);
-	__set_rig_info_power(tx, info.power);
-	__set_rig_info_mode(tx, info.tx_mode);
-	__set_rig_info_mode(rx, info.rx_mode);
+	pretty_print_freq(tx_freq, sizeof(tx_freq), qso_get_freq(tx));
+	pretty_print_freq(rx_freq, sizeof(rx_freq), qso_get_freq(rx));
+	pretty_print_power(power, sizeof(power), qso_get_power(tx));
 
-	tx = qso_get_tx_side(contest_params->template);
-	rx = qso_get_rx_side(contest_params->template);
-
-	__set_rig_info_freq(tx, info.tx_freq);
-	__set_rig_info_freq(rx, info.rx_freq);
-	__set_rig_info_power(tx, info.power);
-	__set_rig_info_mode(tx, info.tx_mode);
-	__set_rig_info_mode(rx, info.rx_mode);
-
-	prev_gen = info.gen;
+	tx_mode = qso_get_mode(tx);
+	rx_mode = qso_get_mode(rx);
 
-	msg_print(MSG_RIG, CE_DEBUG, "Rig: tx %.6f %s @ %.3f W, rx %.6f %s",
-		  info.tx_freq / 1e6,
-		  hamlib_mode_name_raw(info.tx_mode),
-		  info.power / 1000.,
-		  info.rx_freq / 1e6,
-		  hamlib_mode_name_raw(info.rx_mode));
-}
+	msg_print(MSG_RIG, CE_DEBUG, "Rig: tx %s %s @ %s, rx %s %s",
+		  tx_freq,
+		  str_cstr(tx_mode),
+		  power,
+		  rx_freq,
+		  str_cstr(rx_mode));
 
-/*
- * periodic info announcements
- */
-static void info_periodic_cb(struct periodic *periodic, uint64_t now,
-			     void *private)
-{
-	const struct contest_params *params = contest_params;
-	struct qso *template = params->template;
-	struct qso_side *tx;
-
-	tx = qso_get_tx_side(template);
-
-	announce_publish_info(xgethostname(),
-			      str_getref(params->name),
-			      qso_get_operator_call(tx),
-			      qso_get_country(tx),
-			      qso_get_state(tx),
-			      qso_get_county(tx),
-			      qso_get_zone_dxcc(tx),
-			      qso_get_zone_cqz(tx),
-			      qso_get_zone_itu(tx));
+	str_putref(tx_mode);
+	str_putref(rx_mode);
 }
 
 /*

          
@@ 866,90 720,15 @@ static void sync_current(void)
 	}
 }
 
-int contest_init(struct contest_params *params)
-{
-	struct qso *template;
-	int ret;
-
-	contest_params = params;
-
-	MXINIT(&location_lock, &location_lc);
-	MXINIT(&rig_lock, &rig_lc);
-
-	template = qso_alloc();
-	if (!template) {
-		ret = -ENOMEM;
-		goto err;
-	}
-
-	contact = qso_alloc();
-	if (!contact) {
-		ret = -ENOMEM;
-		goto err_template;
-	}
-
-	ret = announce_connect(net_update_row,
-			       handle_qso_notification,
-			       handle_location_notification,
-			       handle_rig_notification);
-	if (ret)
-		goto err_free;
-
-	ret = load_config(AT_FDCWD, template, "contest");
-	if (ret)
-		goto err_ann;
-
-	params->template = template;
-
-	/*
-	 * Note: the periodic timer makes uses of the template!
-	 */
-
-	periodic_init(&info_periodic, info_periodic_cb, NULL);
-	periodic_set_period_sec(&info_periodic, ANNOUNCE_INFO_PERIOD);
-	periodic_arm(&info_periodic);
-
-	return 0;
-
-err_ann:
-	/* disconnect from announcements - a little racy, but good enough */
-	announce_connect(NULL, NULL, NULL, NULL);
-
-err_free:
-	qso_putref(contact);
-
-err_template:
-	qso_putref(template);
-
-err:
-	MXDESTROY(&rig_lock);
-	MXDESTROY(&location_lock);
-
-	return ret;
-}
-
-void contest_cleanup(void)
-{
-	periodic_disarm(&info_periodic);
-
-	/* disconnect from announcements - a little racy, but good enough */
-	announce_connect(NULL, NULL, NULL, NULL);
-
-	qso_putref(contest_params->template);
-	qso_putref(contact);
-
-	MXDESTROY(&rig_lock);
-	MXDESTROY(&location_lock);
-}
-
-int contest_run(struct xlua_state *lua, struct xfield_def *_wfields,
-		size_t _nwfields)
+int contest_ui_run(struct xlua_state *lua, struct contest_params *params,
+		   struct xfield_def *_wfields, size_t _nwfields)
 {
 	char error[128];
 	uint64_t gen;
 	int ret;
 
-	/* stash the field info into the globals */
+	/* stash params into the globals */
+	contest_params = params;
 	wfields = _wfields;
 	nwfields = _nwfields;